home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 January: Mac OS SDK / Dev.CD Jan 00 SDK1.toast / Development Kits / Mac OS / Navigation Services SDK / Examples / SimpleText / SimpleText ƒ / SimpleText.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-06-16  |  145.3 KB  |  5,535 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        SimpleText.c
  3.  
  4.     Copyright:    © 1997-1998 by Apple Computer, Inc., all rights reserved.
  5.  
  6. */
  7.  
  8. /*
  9. **    You may incorporate this sample code into your applications without
  10. **    restriction, though the sample code has been provided "AS IS" and the
  11. **    responsibility for its operation is 100% yours.  However, what you are
  12. **    not permitted to do is to redistribute the source as "DSC Sample Code"
  13. **    after having made changes. If you're going to re-distribute the source,
  14. **    we require that you make it clear in the source that the code was
  15. **    descended from Apple Sample Code, but that you've made changes.
  16. */
  17.  
  18. #include "MacIncludes.h"
  19.  
  20. #include <ImageCompression.h>    // for CustomGetFilePreview
  21. #include <Threads.h>
  22.  
  23. #define CompilingMain=1
  24. #include "SimpleText.h"
  25. #include "Clipboard.h"
  26. #include "AGFile.h"
  27.  
  28. #include "NavigationServicesSupport.h"
  29.  
  30.  
  31. // --------------------------------------------------------------------------------------------------------------
  32. // INTERNAL TYPES AND TYPEDEFS
  33. // --------------------------------------------------------------------------------------------------------------
  34.  
  35.  
  36. // refCon value between SimpleCatchShape and GXInstallQDTranslator    
  37. typedef struct
  38.     {
  39.     gxShape         thePage;
  40.     gxRectangle        thePageRectangle;
  41.     Boolean            doLayout;
  42.     gxJob            theJob;
  43.     } CatchRefCon;
  44.  
  45. // --------------------------------------------------------------------------------------------------------------
  46. // FORWARD DECLARES
  47. // --------------------------------------------------------------------------------------------------------------
  48. OSErr     DoActivate(WindowRef pWindow, Boolean activating);
  49. OSErr    DoCommand(WindowRef pWindow, short commandID, long menuResult);
  50. OSErr    DoKeyEvent(WindowRef pWindow, EventRecord * pEvent, Boolean processPageControls);
  51. Boolean CommandToIDs(short commandID, short * menuID, short *itemID);
  52. void     AdjustMenus(WindowRef pWindow, Boolean editDialogs, Boolean forceTitlesOn);
  53.  
  54. // --------------------------------------------------------------------------------------------------------------
  55. // GLOBAL VARIABLES
  56. // --------------------------------------------------------------------------------------------------------------
  57. EventRecord            gEvent;                    // currently pending event
  58. Boolean                gAllDone;                // true if the application is the in process of terminating
  59. MachineInfoRec        gMachineInfo;            // info about abilities and options installed on this machine
  60. short                gApplicationResFile;    // resource fork of application
  61. RgnHandle            gCursorRgn;                // region to control the cursor apearence
  62. AGRefNum            gAGRefNum = -1;            // AppleGuide database which is open
  63. FSSpec                gAGSpec;                // where to find our database
  64. AGCoachRefNum        gAGCoachRefNum = -1;    // coach handler refNum
  65. FontMappingHandle    gFontMappingList = nil;    // list of font mappings
  66. ThreadID            gFontThread;            // thread that builds font menu
  67. ThreadID            gAGThread;                // thread that looks for AppleGuide database
  68. ThreadID            gStarterThread;            // starts our other threads for us
  69. Boolean                gDontYield;                // whether our threads should yield
  70. void*                gThreadResults;            // scratch space for thread results
  71.  
  72. // These variables are for the find/replace commands
  73. Str255            gFindString = "\p", gReplaceString = "\p";
  74. Boolean            gWrapAround = false, gCaseSensitive = false;
  75.  
  76. // Metrowerks MWCRuntime.lib defines qd for us on PPC, and their
  77. // __runtime module does under the 68K case. OTOH, neither SC nor
  78. // MrC give us qd for free, so we need it there. I'm still not
  79. // certain which way to go for the ThinkC or Symantec PPC case.
  80. #if !defined(__MWERKS__)
  81. // QuickDraw globals
  82. QDGlobals        qd;
  83. #endif
  84.  
  85. // --------------------------------------------------------------------------------------------------------------
  86. #pragma segment Utility
  87.  
  88. static pascal Boolean AlertFilter(DialogRef theDialog, EventRecord *theEvent, short *itemHit)
  89. {
  90.     if (theEvent->what == activateEvt && (DialogRef) theEvent->message == theDialog)
  91.         {
  92.         SetDialogDefaultItem(theDialog, 1);
  93.         }
  94.  
  95.     if (StdFilterProc(theDialog, theEvent, itemHit))
  96.         return true;
  97.  
  98.     // Pass updates through (Activates are tricky...was mucking with Apple menu & thereby
  99.     // drastically changing how the system handles the menu bar during our alert)
  100.     if (theEvent->what == updateEvt /* || theEvent->what == activateEvt */ )
  101.         {
  102.         HandleEvent(theEvent);
  103.         }
  104.  
  105.     return false;
  106.  
  107. } // AlertFilter
  108.  
  109.  
  110. void ConductErrorDialog(OSErr error, short commandID, short alertType)
  111. {
  112.     long        foundError;            // The error, converted to a number
  113.     short        stringIndex;        // Index into the strings
  114.     Str255        errorText;            // the error in a string format
  115.     
  116.     // Start with no error so far
  117.     foundError = 0;
  118.     
  119.     // Start with the first string
  120.     stringIndex = 1;
  121.     
  122.     // Loop until we find an error string
  123.     errorText[0] = 0;
  124.  
  125.     do
  126.         {
  127.         // Get the string, and convert it to a number
  128.         GetIndString(errorText, kErrorBaseID + commandID, stringIndex);
  129.         if (errorText[0] == 0)
  130.             break;
  131.         StringToNum(errorText, &foundError);
  132.         
  133.         // If we reach the last string, or we match the error code
  134.         if ((foundError == 0) ||
  135.             (foundError == error))
  136.             {
  137.             // Get the text string for this error
  138.             GetIndString(errorText, kErrorBaseID + commandID, stringIndex+1);
  139.             }
  140.         else
  141.             {
  142.             // Otherwise, make us continue until we get a string
  143.             errorText[0] = 0;
  144.             }
  145.             
  146.         // Advance so we get the next string number
  147.         stringIndex += 2;
  148.         
  149.         } while (errorText[0] == 0);                // errorText[0] == 0
  150.         
  151.     if (errorText[0] != 0)
  152.         {
  153.         DialogRef    dPtr;
  154.         short        hit;
  155.         
  156.         SetCursor(&qd.arrow);
  157.         ParamText(errorText, "\p", "\p", "\p");
  158.         
  159.         #if !GENERATINGPOWERPC
  160.             if (gMachineInfo.theEnvirons.systemVersion < 0x0700)
  161.                 {
  162.                 short ** hDialog;
  163.                 
  164.                 hDialog = (short**) GetResource('DLOG', kErrorBaseID + alertType);
  165.                 (*hDialog)[4] = dBoxProc;
  166.                 
  167.                 dPtr = GetNewDialog(kErrorBaseID + alertType, nil, (WindowRef)-1);
  168.                 
  169.                 do
  170.                     {
  171.                     ModalDialog(nil, &hit);
  172.                     } while (hit != ok);
  173.                 
  174.                 DisposeDialog(dPtr);
  175.                 }
  176.             else
  177.                 {
  178.                 dPtr = GetNewDialog(kErrorBaseID + alertType, nil, (WindowRef)-1);
  179.                 
  180.                 SetDialogDefaultItem(dPtr, ok);
  181.                 
  182.                 BeginMovableModal();
  183.                 
  184.                 do
  185.                     {
  186.                     MovableModalDialog(nil, &hit);
  187.                     } while (hit != ok);
  188.                 
  189.                 DisposeDialog(dPtr);
  190.                 EndMovableModal();
  191.                 }
  192.         #else
  193.             dPtr = GetNewDialog(kErrorBaseID + alertType, nil, (WindowRef)-1);
  194.             
  195.             SetDialogDefaultItem(dPtr, ok);
  196.             
  197.             BeginMovableModal();
  198.             
  199.             do
  200.                 {
  201.                 MovableModalDialog(nil, &hit);
  202.                 } while (hit != ok);
  203.             
  204.             DisposeDialog(dPtr);
  205.             EndMovableModal();
  206.         #endif
  207.         }
  208.         
  209. } // ConductErrorDialog
  210.  
  211. // --------------------------------------------------------------------------------------------------------------
  212. #pragma segment Utility
  213.  
  214. static void MovableModalMenus(DialogRef dPtr, short *pItem, long menuResult)
  215. {
  216.     short    iCut, iCopy, iClear, iPaste;
  217.     short    editMenu;
  218.     short    menuItem = menuResult & 0xFFFF;
  219.     
  220.     // find out where edit menus are
  221.     CommandToIDs(cCut, &editMenu, &iCut);
  222.     CommandToIDs(cCopy, &editMenu, &iCopy);
  223.     CommandToIDs(cClear, &editMenu, &iClear);
  224.     CommandToIDs(cPaste, &editMenu, &iPaste);
  225.     
  226.     HiliteMenu(0);
  227.     switch (menuResult >> 16)
  228.         {
  229.         case mApple:
  230.             {
  231.             Str255    tempString;
  232.             
  233.             GetMenuItemText(GetMenuHandle(menuResult>>16), menuItem, tempString);
  234.             OpenDeskAcc(tempString);
  235.             }
  236.             break;
  237.             
  238.         case mEdit:
  239.             {
  240.             short    type;
  241.             Handle    item;
  242.             Rect    box;
  243.             short    editField = GetDialogKeyboardFocusItem(dPtr);
  244.             
  245.             // return typed item, if it isn't disabled
  246.             GetDialogItem(dPtr, editField, &type, &item, &box);
  247.             if ((type & itemDisable) == 0)
  248.                 *pItem = editField;
  249.                 
  250.             if (menuItem == iCut)
  251.                 {
  252.                 DialogCut(dPtr);
  253.                 ZeroScrap();
  254.                 TEToScrap();
  255.                 }
  256.                 
  257.             if (menuItem == iCopy)
  258.                 {
  259.                 DialogCopy(dPtr);
  260.                 ZeroScrap();
  261.                 TEToScrap();
  262.                 }
  263.                 
  264.             if (menuItem == iClear)
  265.                 DialogDelete(dPtr);
  266.                 
  267.             if (menuItem == iPaste)
  268.                 DialogPaste(dPtr);
  269.             }
  270.             break;
  271.         }
  272.         
  273. } // MovableModalMenus
  274.  
  275. // --------------------------------------------------------------------------------------------------------------
  276. #pragma segment Utility
  277.  
  278. void MovableModalDialog(ModalFilterProcPtr filterProc, short *pItem)
  279. /*
  280.     Call this as you would ModalDialog, when the dialog is moveable
  281.     modal.
  282.     
  283.     However, first call BeginMovableModal, and afterwards (after
  284.     disposing of dialog) call EndMovableModal.
  285. */
  286. {
  287.     GrafPtr     curPort;
  288.     DialogRef    dPtr = FrontWindow();
  289.     
  290.     *pItem = 0;    
  291.     if (dPtr)
  292.         {
  293.         GetPort(&curPort);
  294.         SetPort(dPtr);
  295.         
  296.         do
  297.             {
  298.             WaitNextEvent(mDownMask + mUpMask + keyDownMask + keyUpMask + autoKeyMask + updateMask + activMask + osMask,
  299.                             &gEvent, 0, nil);
  300.             
  301.             // call the filter proc
  302.             if ( (filterProc) && ((*filterProc) (dPtr, &gEvent, pItem)) )
  303.                 break;
  304.                             
  305.             // call the basic filtering
  306.             if (StdFilterProc(dPtr, &gEvent, pItem))
  307.                 break;
  308.                 
  309.             // handle keyboard
  310.             if ((gEvent.what == keyDown) && (gEvent.modifiers & cmdKey))
  311.                 {
  312.                 MovableModalMenus(dPtr, pItem, MenuKey(gEvent.message & charCodeMask));
  313.                 break;
  314.                 }
  315.                 
  316.             // handle clicks and drags
  317.             if (gEvent.what == mouseDown)
  318.                 {
  319.                 WindowRef    whichWindow;
  320.                 short        part = FindWindow(gEvent.where, &whichWindow);
  321.                 
  322.                 // menu bar events
  323.                 if (part == inMenuBar)
  324.                     {
  325.                     MovableModalMenus(dPtr, pItem, MenuSelect(gEvent.where));
  326.                     break;
  327.                     }
  328.                     
  329.                 // check for outside of our window
  330.                 if (!PtInRgn(gEvent.where, ((WindowPeek)dPtr)->strucRgn))
  331.                     {
  332.                     SysBeep(1);
  333.                     gEvent.what = nullEvent;
  334.                     }
  335.                     
  336.                 // drag the window around
  337.                 if ( (part == inDrag) && (whichWindow == dPtr) )
  338.                     {
  339.                     Rect    tempRect = (**GetGrayRgn()).rgnBBox;
  340.                     
  341.                     DragWindow(GetDialogWindow(dPtr), gEvent.where, &tempRect);
  342.                     gEvent.what = nullEvent;
  343.                     }
  344.                 }
  345.                 
  346.             // check with standard dialog stuff    
  347.             {
  348.             DialogRef    tempDialog;
  349.             
  350.             if ( IsDialogEvent(&gEvent) && DialogSelect(&gEvent, &tempDialog, pItem) )
  351.                 break;
  352.             }
  353.             
  354.             // handle updates
  355.             if (gEvent.what == updateEvt)
  356.                 {
  357.                 HandleEvent(&gEvent);
  358.                 break;
  359.                 }
  360.             } while (true);
  361.         
  362.         SetPort(curPort);
  363.         }
  364.         
  365. } // MovableModalDialog
  366.  
  367. // --------------------------------------------------------------------------------------------------------------
  368. #pragma segment Utility
  369.  
  370. void BeginMovableModal(void)
  371. {
  372.     DialogRef    dPtr = FrontWindow();
  373.     WindowRef    nextWindow = GetNextWindow(dPtr);
  374.     
  375.     if (nextWindow)
  376.         DoActivate(nextWindow, false);
  377.     AdjustMenus(GetDialogWindow(dPtr), (GetDialogKeyboardFocusItem(dPtr) > 0), false);
  378.  
  379. } // BeginMovableModal
  380.  
  381. // --------------------------------------------------------------------------------------------------------------
  382. #pragma segment Utility
  383.  
  384. void EndMovableModal(void)
  385. {
  386.     WindowRef    nextWindow = FrontWindow();
  387.     
  388.     AdjustMenus(nextWindow, true, false);
  389.     if (nextWindow)
  390.         DoActivate(nextWindow, true);
  391.     
  392. } // EndMovableModal
  393.  
  394. // --------------------------------------------------------------------------------------------------------------
  395. #pragma segment Utility
  396.  
  397. short ConductFindOrReplaceDialog(short dialogID)
  398. {
  399.     DialogRef    dPtr;
  400.     short        hit;
  401.     
  402.     dPtr = GetNewDialog(dialogID, nil, (WindowRef)-1);
  403.     if (dPtr)
  404.         {
  405.         short    kind;
  406.         Rect    box;
  407.         Handle    item;
  408.         
  409.         // standard default behavior
  410.         SetDialogDefaultItem(dPtr, ok);
  411.         SetDialogCancelItem (dPtr, cancel);
  412.         SetDialogTracksCursor(dPtr, true);
  413.         
  414.         // Find string
  415.         GetDialogItem(dPtr, iFindEdit, &kind, &item, &box);
  416.         SetDialogItemText(item, gFindString);
  417.  
  418.         // check boxes
  419.         GetDialogItem(dPtr, iCaseSensitive, &kind, &item, &box);
  420.         SetControlValue((ControlRef)item, gCaseSensitive);
  421.         GetDialogItem(dPtr, iWrapAround, &kind, &item, &box);
  422.         SetControlValue((ControlRef)item, gWrapAround);
  423.         
  424.         if (dialogID == kReplaceWindowID)
  425.             {
  426.             // Replace string
  427.             GetDialogItem(dPtr, iReplaceEdit, &kind, &item, &box);
  428.             SetDialogItemText(item, gReplaceString);
  429.             }
  430.         
  431.         // select the search text by default
  432.         SelectDialogItemText(dPtr, iFindEdit, 0, 32767);
  433.         
  434.         // and away we go!
  435.         ShowWindow(GetDialogWindow(dPtr));
  436.         BeginMovableModal();
  437.         
  438.         do
  439.             {
  440.             MovableModalDialog(nil, &hit);
  441.             switch (hit)
  442.                 {
  443.                 case iCaseSensitive:
  444.                 case iWrapAround:
  445.                     GetDialogItem(dPtr, hit, &kind, &item, &box);
  446.                     SetControlValue((ControlRef)item, 1-GetControlValue((ControlRef)item));
  447.                     break;
  448.                 }
  449.             } while ( (hit != ok) && (hit != cancel) && (hit != iReplaceAll) );
  450.         
  451.         if (hit != cancel)
  452.             {
  453.             // Find string
  454.             GetDialogItem(dPtr, iFindEdit, &kind, &item, &box);
  455.             GetDialogItemText(item, gFindString);
  456.     
  457.             // check boxes
  458.             GetDialogItem(dPtr, iCaseSensitive, &kind, &item, &box);
  459.             gCaseSensitive = GetControlValue((ControlRef)item);
  460.             GetDialogItem(dPtr, iWrapAround, &kind, &item, &box);
  461.             gWrapAround = GetControlValue((ControlRef)item);
  462.             
  463.             if (dialogID == kReplaceWindowID)
  464.                 {
  465.                 // Replace string
  466.                 GetDialogItem(dPtr, iReplaceEdit, &kind, &item, &box);
  467.                 GetDialogItemText(item, gReplaceString);
  468.                 }
  469.             }
  470.             
  471.         DisposeDialog(dPtr);
  472.         EndMovableModal();
  473.         }
  474.         
  475.     return(hit);
  476.     
  477. } // ConductFindOrReplaceDialog
  478.  
  479. // --------------------------------------------------------------------------------------------------------------
  480. #pragma segment Utility
  481.  
  482. void SetWatchCursor(void)
  483. {
  484.     CursHandle    theWatch;
  485.         
  486.     theWatch = GetCursor(watchCursor);
  487.     if (theWatch)
  488.         {
  489.         char    oldState;
  490.         
  491.         oldState = HGetState((Handle) theWatch);
  492.         HLock((Handle) theWatch);
  493.         SetCursor(*theWatch);
  494.         HSetState((Handle) theWatch, oldState);
  495.         }
  496.         
  497. } // SetWatchCursor
  498.  
  499. // --------------------------------------------------------------------------------------------------------------
  500. #pragma segment Utility
  501.  
  502. void LongRectToRect(LongRect* longRect, Rect *rect)
  503. {
  504.     rect->top         = longRect->top;
  505.     rect->left         = longRect->left;
  506.     rect->bottom     = longRect->bottom;
  507.     rect->right     = longRect->right;
  508.     
  509. } // LongRectToRect
  510.  
  511. // --------------------------------------------------------------------------------------------------------------
  512. #pragma segment Utility
  513.  
  514. void RectToLongRect(Rect *rect, LongRect *longRect)
  515. {
  516.     longRect->top         = rect->top;
  517.     longRect->left         = rect->left;
  518.     longRect->bottom     = rect->bottom;
  519.     longRect->right     = rect->right;
  520.     
  521. } // RectToLongRect
  522.  
  523. // --------------------------------------------------------------------------------------------------------------
  524. #pragma segment Utility
  525.  
  526. void GetPICTRectangleAt72dpi(PicHandle hPicture, Rect *pictureRect)
  527. {
  528.     typedef struct FixedRect {
  529.         Fixed left;
  530.         Fixed top;
  531.         Fixed right;
  532.         Fixed bottom;
  533.     } FixedRect;
  534.     
  535.     typedef struct {
  536.         Picture                pictInfo;
  537.         unsigned short        versionOp;        // 0x1101
  538.         Byte                opCodes[1];
  539.     } PICTHeaderVer1;
  540.     
  541.     typedef struct {
  542.         Picture            pictInfo;
  543.         unsigned short    versionOp;        // 0x0011
  544.         unsigned short    versionOp2;        // 0x02ff
  545.         unsigned short    headerOp;        // 0x0c00
  546.         unsigned short    version;        // 0xffff
  547.         unsigned short    version2;        // 0xffff
  548.         FixedRect        pictBounds;
  549.         unsigned long    reserved;
  550.         unsigned short    opCodes[1];
  551.     } PICTHeaderVer2;
  552.     
  553.     typedef struct {
  554.         Picture            pictInfo;
  555.         unsigned short    versionOp;        // 0x0011
  556.         unsigned short    versionOp2;        // 0x02ff
  557.         unsigned short    headerOp;        // 0x0c00
  558.         unsigned short    version;        // 0xfffe
  559.         unsigned short    reserved;        // 0x0000
  560.         Fixed            hRes;
  561.         Fixed            vRes;
  562.         Rect            pictBounds;
  563.         unsigned long    reserved2;
  564.         unsigned short    opCodes[1];
  565.     } PICTHeaderVer2Ext;
  566.  
  567.     Fixed            hRes, vRes;
  568.     PICTHeaderVer1* pPict = (PICTHeaderVer1*) *hPicture;
  569.  
  570.     hRes = vRes = ff(72);        // assume 72 dpi
  571.  
  572.     if (pPict->versionOp == 0x0011) 
  573.         {    
  574.         // Version 2 PICT
  575.     
  576.         PICTHeaderVer2* pPict2 = (PICTHeaderVer2*) pPict;
  577.         
  578.         if (pPict2->version == 0xfffe) 
  579.             {    
  580.             // Extended Version 2
  581.             PICTHeaderVer2Ext* pPict2ext = (PICTHeaderVer2Ext*) pPict;
  582.             hRes = pPict2ext->hRes;
  583.             vRes = pPict2ext->vRes;
  584.             }
  585.         }
  586.  
  587.     hRes = FixDiv(hRes, ff(72));
  588.     vRes = FixDiv(vRes, ff(72));
  589.     pictureRect->left     = Fix2Long(FixDiv( ff((**hPicture).picFrame.left),         hRes ));
  590.     pictureRect->right     = Fix2Long(FixDiv( ff((**hPicture).picFrame.right),     hRes ));
  591.     pictureRect->top     = Fix2Long(FixDiv( ff((**hPicture).picFrame.top),         vRes ));
  592.     pictureRect->bottom = Fix2Long(FixDiv( ff((**hPicture).picFrame.bottom),     vRes ));
  593.     
  594. } // GetPICTRectangleAt72dpi
  595.  
  596. // --------------------------------------------------------------------------------------------------------------
  597. #pragma segment Utility
  598.  
  599. static WindowDataPtr    GetWindowInfo(WindowRef pWindow)
  600. {
  601.     WindowDataPtr result = nil;
  602.     
  603.     if     (
  604.         (pWindow) &&
  605.         (GetWindowKind(pWindow) == userKind)
  606.         )
  607.         result = (WindowDataPtr) GetWRefCon(pWindow);
  608.  
  609.     return result;
  610.     
  611. } // GetWindowInfo
  612.  
  613. // --------------------------------------------------------------------------------------------------------------
  614. #pragma segment Utility
  615.  
  616. static short ZeroStringSub(Str255 destString, Str255 subStr)
  617.     // returns number of substitutions performed
  618. {
  619.     OSErr    anErr;
  620.     Handle    destHandle = nil;
  621.     Handle    subHandle = nil;
  622.     short    count = 0;
  623.  
  624.     anErr = PtrToHand(&destString[1], &destHandle, destString[0]);
  625.     if (anErr == noErr)
  626.         {        
  627.         anErr = PtrToHand(&subStr[1], &subHandle, subStr[0]);
  628.         if (anErr == noErr)
  629.             {
  630.             count = ReplaceText(destHandle, subHandle, "\p^0");        // error or # of substitutions
  631.                         
  632.             destString[0] = GetHandleSize(destHandle);
  633.             BlockMoveData(*destHandle, &destString[1], destString[0]);
  634.             }
  635.         }
  636.  
  637.     DisposeHandle(destHandle);
  638.     DisposeHandle(subHandle);
  639.  
  640.     if (count < 0)
  641.         count = 0;        // change error code into count = 0 substitutions
  642.  
  643.     return count;
  644.  
  645. } // ZeroStringSub
  646.  
  647. // --------------------------------------------------------------------------------------------------------------
  648. // SEARCH/REPLACE UTILITY FUNCTIONS
  649. // --------------------------------------------------------------------------------------------------------------
  650. static Boolean IsThisTheString(
  651.             Ptr p,                        // pointer to check
  652.             Str255 searchString,        // string to check for
  653.             Boolean isCaseSensitive)    // case sensitive check or not
  654. /*
  655.     Returns true if the supplied string is at the specified offset.
  656.     Otherwise returns false.
  657. */
  658. {
  659.     Boolean    returnValue = false;
  660.     
  661.     if (isCaseSensitive)
  662.         returnValue = ( IUMagString(p, &searchString[1], searchString[0], searchString[0]) == 0 );
  663.     else
  664.         returnValue = ( IUMagIDString(p, &searchString[1], searchString[0], searchString[0]) == 0 );
  665.         
  666.     return(returnValue);
  667.     
  668. } // IsThisTheString
  669.  
  670. // --------------------------------------------------------------------------------------------------------------
  671.  
  672. Boolean PerformSearch(
  673.         Handle    h,                    // handle to search
  674.         long start,                    // offset to begin with
  675.         Str255 searchString,        // string to search for
  676.         Boolean isCaseSensitive,    // case sensitive search
  677.         Boolean isBackwards,        // search backwards from starting point
  678.         Boolean isWraparound,        // wrap search around from end->begining
  679.         long * pNewStart,            // returned new selection start
  680.         long * pNewEnd)                // returned new selection end
  681. /*
  682.     Performs a search on the supplied handle, starting at the provided
  683.     offset.  Returns the new selection start and end values, and true
  684.     if the search is successful.  Otherwise it returns false.
  685. */
  686. {
  687.     char    flags;
  688.     Ptr        startPtr;
  689.     Ptr        endPtr;
  690.     Ptr        searchPtr;
  691.     Boolean    foundIt = false;
  692.     
  693.     flags = HGetState(h);
  694.     HLock(h);
  695.             
  696.     // back up one when searching backwards, or we'll hit every time on the current
  697.     // character
  698.     if (isBackwards)
  699.         {
  700.         if (start != 0)
  701.             {
  702.             --start;
  703.             }
  704.         else
  705.             {
  706.             if (isWraparound)
  707.                 start = GetHandleSize(h);
  708.             else
  709.                 return(false);
  710.             }
  711.         }
  712.         
  713.     // determine the bounds of the searching
  714.     startPtr = (*h) + start;
  715.     if ( isWraparound )
  716.         {
  717.         if (isBackwards)
  718.             {
  719.             // go backwards until just after the start, or begining of
  720.             // document is start is the end
  721.             if (start == GetHandleSize(h))
  722.                 endPtr = *h;
  723.             else
  724.                 endPtr = startPtr + 1;
  725.             }
  726.         else
  727.             {
  728.             // go forwards until just before the start, or to the end
  729.             // of the document is the start is already the begining
  730.             if (start == 0)
  731.                 endPtr = *h + GetHandleSize(h);
  732.             else
  733.                 endPtr = startPtr - 1;
  734.             }
  735.         }
  736.     else
  737.         {
  738.         if (isBackwards)
  739.             {
  740.             // go back until hit begining of document
  741.             endPtr = *h-1;    
  742.             }
  743.         else
  744.             {
  745.             // go forward until hit end of document
  746.             endPtr = *h + GetHandleSize(h);
  747.             }
  748.         }
  749.         
  750.     searchPtr = startPtr;
  751.     while (searchPtr != endPtr)
  752.         {
  753.         if (IsThisTheString(searchPtr, searchString, isCaseSensitive))
  754.             {
  755.             foundIt = true;
  756.             *pNewStart = searchPtr - *h;
  757.             *pNewEnd = *pNewStart + searchString[0];
  758.             break;
  759.             }
  760.             
  761.         if (isBackwards)
  762.             --searchPtr;
  763.         else
  764.             ++searchPtr;
  765.             
  766.         if (isWraparound)
  767.             {
  768.             if (searchPtr < *h)
  769.                 searchPtr = *h + GetHandleSize(h);
  770.             if (searchPtr > *h + GetHandleSize(h))
  771.                 searchPtr = *h;
  772.             }
  773.         }
  774.         
  775.     HSetState(h, flags);
  776.     
  777.     return(foundIt);
  778.     
  779. } // PerformSearch
  780.  
  781. // --------------------------------------------------------------------------------------------------------------
  782. // SELECTION UTILITY ROUTINES
  783. // --------------------------------------------------------------------------------------------------------------
  784. void DrawSelection(WindowDataPtr pData, Rect *pSelection, short * pPhase, Boolean bumpPhase)
  785. {
  786.     if    (!EmptyRect(pSelection) ) 
  787.         {
  788.         RgnHandle    oldClip = NewRgn();
  789.         Pattern        aPattern;
  790.         Rect        newClip;
  791.  
  792.         
  793.         if     ( 
  794.             (bumpPhase) && 
  795.             (MOVESELECTION(TickCount()) ) 
  796.             )
  797.             {
  798.             if ((++(*pPhase)) > 7 )
  799.                 *pPhase = 1;
  800.             }
  801.             
  802.         // setup for drawing in this window
  803.         SetPort((GrafPtr) pData);
  804.         GetClip(oldClip);
  805.         PenMode(notPatXor);
  806.         
  807.         // offset the draw area (SetOrigin a must to preserve pattern appearence)
  808.         // and the clip area to avoid stepping on the scroll bars
  809.         SetOrigin(GetControlValue(pData->hScroll), GetControlValue(pData->vScroll));
  810.         newClip = pData->contentRect;
  811.         OffsetRect(&newClip, GetControlValue(pData->hScroll), GetControlValue(pData->vScroll));
  812.         ClipRect(&newClip);
  813.         
  814.         // do the draw
  815.         GetIndPattern(&aPattern, kPatternListID, (*pPhase)+1);
  816.         PenPat(&aPattern);
  817.         FrameRect(pSelection);
  818.         SetOrigin(0, 0);
  819.         
  820.         // restore the old port settings
  821.         SetClip(oldClip);
  822.         DisposeRgn(oldClip);
  823.         PenNormal();
  824.  
  825.         }
  826.  
  827. } // DrawSelection
  828.  
  829. // --------------------------------------------------------------------------------------------------------------
  830. OSErr SelectContents(WindowRef pWindow, WindowDataPtr pData, EventRecord *pEvent, Rect *pSelection, Rect *pContent, short *pPhase)
  831. {
  832.  
  833.     OSErr            anErr = noErr;
  834.     Point            clickPoint = pEvent->where;
  835.     Point            currentPoint;
  836.     Boolean         didJustScroll;
  837.     ControlRef        theControl;
  838.     
  839.     GlobalToLocal(&clickPoint);
  840.     if (FindControl(clickPoint, pWindow, &theControl) == 0)
  841.         {
  842.     
  843.         // move the click point into the proper range
  844.         clickPoint.h += GetControlValue(pData->hScroll);
  845.         clickPoint.v += GetControlValue(pData->vScroll);
  846.         
  847.         // if the shift key is held down then the selection starts from
  848.         // a preexisting point such that we are doing an expand/contract
  849.         // of the original selection
  850.         if (pEvent->modifiers & shiftKey)
  851.             {
  852.             if (clickPoint.h < pSelection->right)
  853.                 clickPoint.h = pSelection->right;
  854.             else
  855.                 clickPoint.h = pSelection->left;
  856.  
  857.             if (clickPoint.v < pSelection->bottom)
  858.                 clickPoint.v = pSelection->bottom;
  859.             else
  860.                 clickPoint.v = pSelection->top;
  861.             }
  862.                         
  863.         while (StillDown())
  864.             {                    
  865.             // get the current mouse 
  866.             GetMouse(¤tPoint);
  867.             
  868.             didJustScroll = false;
  869.             // scroll contents if needed
  870.             {
  871.             short    deltaH = 0;
  872.             short    deltaV = 0;
  873.             
  874.             if (currentPoint.h < 0)
  875.                 deltaH = pData->hScrollAmount;
  876.             if (currentPoint.h > qd.thePort->portRect.right)
  877.                 deltaH = -pData->hScrollAmount;
  878.             if (currentPoint.v < 0)
  879.                 deltaV = pData->vScrollAmount;
  880.             if (currentPoint.v > qd.thePort->portRect.bottom)
  881.                 deltaV = -pData->vScrollAmount;
  882.                 
  883.             if ( (deltaH != 0) || (deltaV != 0) )
  884.                 {                
  885.                 if (deltaH)
  886.                     SetControlAndClipAmount(pData->hScroll, &deltaH);
  887.                 if (deltaV)
  888.                     SetControlAndClipAmount(pData->vScroll, &deltaV);
  889.  
  890.                 DoScrollContent(pWindow, pData, deltaH, deltaV);
  891.                 
  892.                 didJustScroll = true;
  893.                 }
  894.             }
  895.             
  896.             // map mouse into proper range
  897.             currentPoint.h += GetControlValue(pData->hScroll);
  898.             currentPoint.v += GetControlValue(pData->vScroll);
  899.     
  900.             // clip to the document size
  901.             if (currentPoint.h < 0)
  902.                 currentPoint.h = 0;
  903.             if (currentPoint.v < 0)
  904.                 currentPoint.v = 0;
  905.             if (currentPoint.h > pContent->right)
  906.                 currentPoint.h = pContent->right;
  907.             if (currentPoint.v > pContent->bottom)
  908.                 currentPoint.v = pContent->bottom;
  909.                 
  910.             // draw the new selection if it is time or we are about to 
  911.             // exit this loop
  912.             if ((MOVESELECTION(TickCount())) || (!Button()) || (didJustScroll) )
  913.                 {
  914.                 // first, erase any old selection we might have had
  915.                 DrawSelection(pData, pSelection, pPhase, false);
  916.  
  917.                 // make a rectangle out of the two points
  918.                 pSelection->left     = Min(currentPoint.h, clickPoint.h);
  919.                 pSelection->right     = Max(currentPoint.h, clickPoint.h);
  920.                 pSelection->top     = Min(currentPoint.v, clickPoint.v);
  921.                 pSelection->bottom     = Max(currentPoint.v, clickPoint.v);
  922.     
  923.                 // draw the new selection
  924.                 DrawSelection(pData, pSelection, pPhase, true);
  925.                 }
  926.             }
  927.         
  928.         // we handled the selection
  929.         anErr = eActionAlreadyHandled;
  930.         }
  931.         
  932.     return(anErr);
  933.     
  934. } // SelectContents
  935.  
  936. // --------------------------------------------------------------------------------------------------------------
  937. void DragAndDropArea(WindowRef pWindow, WindowDataPtr pData, EventRecord* event, Rect *pFrameRect)
  938. {
  939.     RgnHandle        hilightRgn;
  940.     Rect            r;
  941.     DragReference    theDrag;
  942.     OSErr            anErr = noErr;
  943.     
  944.     if (NewDrag(&theDrag) == noErr)
  945.         {
  946.         if (pData->pDragAddFlavors)
  947.             anErr = (*(pData->pDragAddFlavors)) (pWindow, pData, theDrag);
  948.         
  949.         if (anErr == noErr)
  950.             {
  951.             Rect    globalRect = *pFrameRect;
  952.             
  953.             hilightRgn = NewRgn();    
  954.             LocalToGlobal(&TopLeft(globalRect));
  955.             LocalToGlobal(&BotRight(globalRect));
  956.             RectRgn(hilightRgn, &globalRect);
  957.             SetDragItemBounds(theDrag, 1, &r);
  958.     
  959.             // turn the region from a fill into a frame
  960.             {    
  961.                 RgnHandle tempRgn = NewRgn();
  962.     
  963.                 CopyRgn(hilightRgn, tempRgn);
  964.                 InsetRgn(tempRgn, 1, 1);
  965.                 DiffRgn(hilightRgn, tempRgn, hilightRgn);
  966.                 DisposeRgn(tempRgn);
  967.             }
  968.             
  969.             TrackDrag(theDrag, event, hilightRgn);
  970.             DisposeDrag(theDrag);
  971.             DisposeRgn(hilightRgn);
  972.             }
  973.         }
  974.  
  975. } // DragAndDropArea
  976.  
  977. // --------------------------------------------------------------------------------------------------------------
  978. // WINDOW UTILITY ROUTINES
  979. // --------------------------------------------------------------------------------------------------------------
  980. #pragma segment Main
  981.  
  982. static void CalculateGrowIcon(WindowDataPtr pData, Rect * location)
  983. {
  984.     if (pData->vScroll)
  985.         location->top = (**pData->vScroll).contrlRect.bottom;
  986.     else
  987.         {
  988.         if (pData->hScroll)
  989.             location->top = (**pData->hScroll).contrlRect.top;
  990.         else
  991.             location->top = pData->theWindow.port.portRect.bottom - 15;
  992.         }
  993.         
  994.     if (pData->hScroll)
  995.         location->left = (**pData->hScroll).contrlRect.right;
  996.     else
  997.         {
  998.         if (pData->vScroll)
  999.             location->left = (**pData->vScroll).contrlRect.left;
  1000.         else
  1001.             location->left = pData->theWindow.port.portRect.right - 15;
  1002.         }
  1003.         
  1004.     location->right = location->left + 16;
  1005.     location->bottom = location->top + 16;
  1006.     
  1007. } // CalculateGrowIcon
  1008.  
  1009. // --------------------------------------------------------------------------------------------------------------
  1010. #pragma segment Main
  1011.  
  1012. OSErr    AdjustScrollBars(WindowRef pWindow,
  1013.     Boolean moveControls,                 // might the controls have moved?
  1014.     Boolean didResize,                     // did we just resize the window?
  1015.     Boolean *needInvalidate)            // does the caller need to invalidate contents as a result?
  1016. {
  1017.     OSErr            anErr = noErr;
  1018.     LongRect        docRect;
  1019.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  1020.     Rect            growIconRect;
  1021.     
  1022.     if (needInvalidate)
  1023.         *needInvalidate = false;
  1024.  
  1025.     if (pData)
  1026.         {
  1027.         short    oldHMax, oldVMax;
  1028.         short    oldHValue, oldVValue;
  1029.         
  1030.         // cache current values, we'll force an update if we needed to change em!
  1031.         if (pData->hScroll)
  1032.             {
  1033.             oldHMax = GetControlMaximum(pData->hScroll);
  1034.             oldHValue = GetControlValue(pData->hScroll);
  1035.             }
  1036.         if (pData->vScroll)
  1037.             {
  1038.             oldVMax = GetControlMaximum(pData->vScroll);
  1039.             oldVValue = GetControlValue(pData->vScroll);
  1040.             }
  1041.             
  1042.         // if we have a grow box but not all controls we have to invalidate the grow bar areas
  1043.         // by caclulating them
  1044.         if ( (didResize) && (pData->hasGrow) )
  1045.             {
  1046.             // if we regrow without any scroll bars, we need to update the content area
  1047.             if ( (needInvalidate) && (pData->hScroll == nil) && (pData->vScroll == nil) )
  1048.                 *needInvalidate = true;
  1049.             
  1050.             // invalidate old grow bar areas
  1051.             if (pData->vScroll == nil)
  1052.                 {
  1053.                 growIconRect = GetWindowPort(pWindow)->portRect;
  1054.                 growIconRect.left = pData->contentRect.right;
  1055.                 InvalRect(&growIconRect);
  1056.                 }
  1057.             if (pData->hScroll == nil)
  1058.                 {
  1059.                 growIconRect = GetWindowPort(pWindow)->portRect;
  1060.                 growIconRect.top = pData->contentRect.bottom;
  1061.                 InvalRect(&growIconRect);
  1062.                 }
  1063.             
  1064.             // invalidate new grow bar areas
  1065.             if (pData->vScroll == nil)
  1066.                 {
  1067.                 growIconRect = GetWindowPort(pWindow)->portRect;
  1068.                 growIconRect.left = growIconRect.right - kScrollBarSize;
  1069.                 InvalRect(&growIconRect);
  1070.                 }
  1071.             if (pData->hScroll == nil)
  1072.                 {
  1073.                 growIconRect = GetWindowPort(pWindow)->portRect;
  1074.                 growIconRect.top = growIconRect.bottom - kScrollBarSize;
  1075.                 InvalRect(&growIconRect);
  1076.                 }
  1077.             }
  1078.             
  1079.         // if the controls need moving, recalculate the visible area
  1080.         if (moveControls)
  1081.             {
  1082.             pData->contentRect = GetWindowPort(pWindow)->portRect;
  1083.             if ((pData->hScroll) || (pData->hasGrow) )
  1084.                 pData->contentRect.bottom -= kScrollBarSize;
  1085.             if ((pData->vScroll) || (pData->hasGrow) )
  1086.                 pData->contentRect.right -= kScrollBarSize;
  1087.             }
  1088.             
  1089.         // before doing anything, make the controls invisible
  1090.         if (pData->hScroll)
  1091.             (**pData->hScroll).contrlVis = 0;    
  1092.         if (pData->vScroll)
  1093.             (**pData->vScroll).contrlVis = 0;
  1094.  
  1095.         // based on document and visiable area, adjust possible control values
  1096.         if ( (pData->pGetDocumentRect) && ((pData->hScroll) || (pData->vScroll)) )
  1097.             {
  1098.             // let the object calc the size and content if it wishes to
  1099.             anErr = (*(pData->pGetDocumentRect)) (pWindow, pData, &docRect, false);
  1100.             if (anErr == noErr)
  1101.                 {
  1102.                 short    amountOver;
  1103.                 short    newMax;
  1104.                 
  1105.                 amountOver = (docRect.right - docRect.left) - (pData->contentRect.right - pData->contentRect.left);
  1106.                 if     (
  1107.                     (pData->hScroll) &&
  1108.                     (amountOver > 0)
  1109.                     )
  1110.                     newMax = amountOver;
  1111.                 else
  1112.                     newMax = 0;
  1113.     
  1114.                 if (pData->hScroll)
  1115.                     {
  1116.                     if (GetControlValue(pData->hScroll) > newMax)
  1117.                         {
  1118.                         if (needInvalidate)
  1119.                             *needInvalidate = true;
  1120.                         }
  1121.                     SetControlMaximum(pData->hScroll, newMax);
  1122.                     }
  1123.                 
  1124.                 amountOver = (docRect.bottom - docRect.top) - (pData->contentRect.bottom - pData->contentRect.top);
  1125.                 if     (
  1126.                     (pData->vScroll) &&
  1127.                     (amountOver > 0)
  1128.                     )
  1129.                     newMax = amountOver;
  1130.                 else
  1131.                     newMax = 0;
  1132.                     
  1133.                 if (pData->vScroll)
  1134.                     {
  1135.                     if (GetControlValue(pData->vScroll) > newMax)
  1136.                         {
  1137.                         if (needInvalidate)
  1138.                             *needInvalidate = true;
  1139.                         }
  1140.                     SetControlMaximum(pData->vScroll, newMax);
  1141.                     }
  1142.                 }
  1143.             }
  1144.             
  1145.         // then, if the controls need moving, we move them and inval the old
  1146.         // and new locations
  1147.         if (moveControls)
  1148.             {
  1149.             // if we have grow box we invalidate the old grow location
  1150.             if ( pData->hasGrow) 
  1151.                 {
  1152.                 CalculateGrowIcon(pData, &growIconRect);
  1153.                 InvalRect(&growIconRect);
  1154.                 }
  1155.                 
  1156.             if (pData->hScroll)
  1157.                 {
  1158.                 short    widthAdjust;
  1159.                 
  1160.                 if ((pData->vScroll) || (pData->hasGrow))
  1161.                     widthAdjust = -kGrowScrollAdjust;
  1162.                 else
  1163.                     widthAdjust = -1;
  1164.                     
  1165.                 growIconRect = (**pData->hScroll).contrlRect;
  1166.                 InvalRect(&growIconRect);
  1167.                 
  1168.                 MoveControl(pData->hScroll, pData->hScrollOffset-1, GetWindowPort(pWindow)->portRect.bottom - kScrollBarSize);
  1169.                 SizeControl(pData->hScroll, (GetWindowPort(pWindow)->portRect.right - 
  1170.                             GetWindowPort(pWindow)->portRect.left) + widthAdjust - pData->hScrollOffset,
  1171.                             16);
  1172.  
  1173.                 growIconRect = (**pData->hScroll).contrlRect;
  1174.                 InvalRect(&growIconRect);
  1175.                 }
  1176.  
  1177.             if (pData->vScroll)
  1178.                 {
  1179.                 short    heightAdjust;
  1180.                 
  1181.                 if ((pData->hScroll) || (pData->hasGrow))
  1182.                     heightAdjust = -kGrowScrollAdjust;
  1183.                 else
  1184.                     heightAdjust = -1;
  1185.                     
  1186.                 growIconRect = (**pData->vScroll).contrlRect;
  1187.                 InvalRect(&growIconRect);
  1188.  
  1189.                 MoveControl(pData->vScroll, GetWindowPort(pWindow)->portRect.right - kScrollBarSize, pData->vScrollOffset-1);
  1190.                 SizeControl(pData->vScroll, 16,
  1191.                             (GetWindowPort(pWindow)->portRect.bottom - 
  1192.                             GetWindowPort(pWindow)->portRect.top) + heightAdjust - pData->vScrollOffset);
  1193.                 growIconRect = (**pData->vScroll).contrlRect;
  1194.                 InvalRect(&growIconRect);
  1195.                 }
  1196.                 
  1197.             // if we have scroll bars, update the grow icon
  1198.             if ( pData->hasGrow )
  1199.                 {
  1200.                 CalculateGrowIcon(pData, &growIconRect);
  1201.                 InvalRect(&growIconRect);
  1202.                 }
  1203.             
  1204.             }
  1205.  
  1206.         // let the document adjust anything it needs to
  1207.         if (pData->pAdjustSize)
  1208.             anErr = (*(pData->pAdjustSize)) (pWindow, pData, &didResize);
  1209.             
  1210.         if ((didResize) && (needInvalidate))
  1211.             *needInvalidate = true;
  1212.  
  1213.  
  1214.         if ( ((WindowPeek) pWindow)->hilited )
  1215.             {
  1216.             // after doing something, make the controls visible
  1217.             if (pData->hScroll)
  1218.                 {
  1219.                 if ((oldHMax != GetControlMaximum(pData->hScroll)) || (oldHValue != GetControlValue(pData->hScroll)) )
  1220.                     ShowControl(pData->hScroll);
  1221.                 else
  1222.                     (**pData->hScroll).contrlVis = 0xFF;    
  1223.                 }
  1224.             if (pData->vScroll)
  1225.                 {
  1226.                 if ((oldVMax != GetControlMaximum(pData->vScroll)) || (oldVValue != GetControlValue(pData->vScroll)) )
  1227.                     ShowControl(pData->vScroll);
  1228.                 else
  1229.                     (**pData->vScroll).contrlVis = 0xFF;
  1230.                 }
  1231.             }
  1232.  
  1233.         }
  1234.         
  1235.     return anErr;
  1236.     
  1237. } // AdjustScrollBars
  1238.  
  1239. // --------------------------------------------------------------------------------------------------------------
  1240. // MENU UTILITY ROUTINES
  1241. // --------------------------------------------------------------------------------------------------------------
  1242. #pragma segment Main
  1243.  
  1244. Boolean CommandToIDs(short commandID, short * menuID, short *itemID)
  1245. {
  1246.  
  1247.     short    ** commandHandle;
  1248.     short    whichMenu;
  1249.     short    oldResFile = CurResFile();
  1250.     Boolean    returnValue = false;
  1251.     
  1252.     UseResFile(gApplicationResFile);
  1253.     for (whichMenu = mApple; whichMenu <= mLastMenu; whichMenu++)
  1254.         {
  1255.         commandHandle = (short**) Get1Resource('MCMD', whichMenu);
  1256.         if (commandHandle)
  1257.             {
  1258.             short    * pCommands = *commandHandle;
  1259.             short    commandIndex;
  1260.             short    numCommands = pCommands[0];
  1261.             
  1262.             for (commandIndex = 1; commandIndex <= numCommands; ++commandIndex)
  1263.                 if (pCommands[commandIndex] == commandID)
  1264.                     {
  1265.                     *menuID = whichMenu;
  1266.                     *itemID = commandIndex;
  1267.                     
  1268.                     returnValue = (commandIndex == numCommands);
  1269.                     }
  1270.             }    
  1271.         }
  1272.         
  1273.     UseResFile(oldResFile);
  1274.     
  1275.     return returnValue;
  1276.     
  1277. } // CommandToIDs
  1278.  
  1279. // --------------------------------------------------------------------------------------------------------------
  1280. #pragma segment Main
  1281.  
  1282. Boolean IsCommandEnabled(short commandID)
  1283. /*
  1284.     returns true if a given command is currently enabled
  1285. */
  1286. {
  1287.     short        whichMenu, whichItem;
  1288.     MenuHandle    menu;
  1289.     
  1290.     CommandToIDs(commandID, &whichMenu, &whichItem);
  1291.     menu = GetMenuHandle(whichMenu);
  1292.     
  1293.     if ((**menu).enableFlags & (1 << whichItem))
  1294.         return(true);
  1295.     
  1296.     return(false);
  1297.     
  1298. } // IsCommandEnabled
  1299.  
  1300. // --------------------------------------------------------------------------------------------------------------
  1301. #pragma segment Main
  1302.  
  1303. void EnableCommand(short commandID)
  1304. /*
  1305.     Given a command ID, enables the first menu item with that command ID.
  1306.     
  1307.     If the command table for a given menu is less than the number of items in the menu,
  1308.     and the command being enabled is the last item in the command table, then all
  1309.     items from there on down are also enabled.  This is useful for menus that get
  1310.     appended to, such as the desk accessory list, font list, or speaking voices list.
  1311. */
  1312. {
  1313.     short    whichMenu;
  1314.     short    whichItem;
  1315.     
  1316.     if (CommandToIDs(commandID, &whichMenu, &whichItem))
  1317.         {
  1318.         short        i;
  1319.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1320.         
  1321.         if (menu)
  1322.             {
  1323.             short        numItems = CountMItems(menu);
  1324.             
  1325.             for (i = whichItem; i <= numItems; ++i)
  1326.                 EnableItem(menu, i);
  1327.             }
  1328.         }
  1329.     else
  1330.         {
  1331.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1332.  
  1333.         if (menu)
  1334.             EnableItem(menu, whichItem);
  1335.         }
  1336.         
  1337. } // EnableCommand
  1338.  
  1339. // --------------------------------------------------------------------------------------------------------------
  1340. #pragma segment Main
  1341.  
  1342. void ChangeCommandName(short commandID, short resourceID, short resourceIndex)
  1343. {
  1344.     short        whichMenu;
  1345.     short        whichItem;
  1346.     MenuHandle    menu;
  1347.     
  1348.     // figure out how this command maps into the menu bar
  1349.     CommandToIDs(commandID, &whichMenu, &whichItem);
  1350.     menu = GetMenuHandle(whichMenu);
  1351.     
  1352.     // then make this item into the requested new string
  1353.     {
  1354.     Str255        theString;
  1355.     
  1356.     GetIndString(theString, resourceID, resourceIndex);
  1357.     SetMenuItemText(menu, whichItem, theString);
  1358.     }
  1359.     
  1360. } // ChangeCommandName
  1361.  
  1362. // --------------------------------------------------------------------------------------------------------------
  1363. #pragma segment Main
  1364.  
  1365. void EnableCommandCheck(short commandID, Boolean check)
  1366. {
  1367.  
  1368.     short    whichMenu;
  1369.     short    whichItem;
  1370.     
  1371.     if (CommandToIDs(commandID, &whichMenu, &whichItem))
  1372.         {
  1373.         short        i;
  1374.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1375.         short        numItems = CountMItems(menu);
  1376.         
  1377.         for (i = whichItem; i <= numItems; ++i)
  1378.             {
  1379.             EnableItem(menu, i);
  1380.             CheckItem(menu, i, check);
  1381.             }
  1382.         }
  1383.     else
  1384.         {
  1385.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1386.  
  1387.         EnableItem(menu, whichItem);
  1388.         CheckItem(menu, whichItem, check);
  1389.         }
  1390.         
  1391. } // EnableCommandCheck
  1392.  
  1393.  
  1394. // --------------------------------------------------------------------------------------------------------------
  1395. #pragma segment Main
  1396.  
  1397. void EnableCommandCheckStyle(short commandID, Boolean check, short style)
  1398. {
  1399.  
  1400.     short    whichMenu;
  1401.     short    whichItem;
  1402.     
  1403.     if (CommandToIDs(commandID, &whichMenu, &whichItem))
  1404.         {
  1405.         short        i;
  1406.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1407.         short        numItems = CountMItems(menu);
  1408.         
  1409.         for (i = whichItem; i <= numItems; ++i)
  1410.             {
  1411.             EnableItem(menu, i);
  1412.             CheckItem(menu, i, check);
  1413.             SetItemStyle(menu, i, style);
  1414.             }
  1415.         }
  1416.     else
  1417.         {
  1418.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1419.  
  1420.         EnableItem(menu, whichItem);
  1421.         CheckItem(menu, whichItem, check);
  1422.         SetItemStyle(menu, whichItem, style);
  1423.         }
  1424.         
  1425. } // EnableCommandCheckStyle
  1426.  
  1427. // --------------------------------------------------------------------------------------------------------------
  1428. #pragma segment Main
  1429.  
  1430. void AdjustMenus(WindowRef pWindow, Boolean editDialogs, Boolean forceTitlesOn)
  1431. {
  1432.     Boolean                 wasEnabled[mNumberMenus];    // Old state of menus
  1433.     short                    whichMenu;                    // for stepping through menus
  1434.     MenuHandle                menu;                        // for reading in menu IDs
  1435.     WindowDataPtr             pData = GetWindowInfo(pWindow);
  1436.     
  1437.     // Step through all of the menus 
  1438.     for (whichMenu = mApple; whichMenu <= mLastMenu; whichMenu++)
  1439.         {
  1440.         // Save the old state of the menu title 
  1441.         menu = GetMenuHandle(whichMenu);
  1442.         if (menu)                                // because contents menu may not be around
  1443.             {
  1444.             if (forceTitlesOn)                
  1445.                 wasEnabled[mLastMenu - whichMenu] = false;
  1446.             else
  1447.                 wasEnabled[mLastMenu - whichMenu] = (((**menu).enableFlags && 1) == 1);
  1448.             
  1449.             // Disable the entire menu 
  1450.             (**menu).enableFlags = 0;        
  1451.             }
  1452.         }
  1453.     
  1454.     // select all, unless someone else changes it
  1455.     ChangeCommandName(cSelectAll, kMiscStrings, iSelectAllCommand);
  1456.  
  1457.     // if we have NO windows, or the current window is one we understand
  1458.     if ((pWindow == nil) || (pData))
  1459.         {
  1460.         // enable the default commands
  1461.         EnableCommand(cAbout);
  1462.         EnableCommand(cDeskAccessory);
  1463.         
  1464.         EnableCommand(cNew);
  1465.         EnableCommand(cOpen);
  1466.         EnableCommand(cQuit);
  1467.     
  1468.         EnableCommand(cShowClipboard);
  1469.         }
  1470.     else
  1471.         {
  1472.         // it's printing or a dialog, so enable cut/copy/paste
  1473.         if (editDialogs)
  1474.             {
  1475.             EnableCommand(cCut);
  1476.             EnableCommand(cCopy);
  1477.             EnableCommand(cPaste);
  1478.             EnableCommand(cClear);
  1479.             }
  1480.         
  1481.         // and desk accs too!        
  1482.         EnableCommand(cDeskAccessory);
  1483.  
  1484.         }
  1485.         
  1486.     if ( (pWindow) && (pData) )
  1487.         {
  1488.         // all windows can be closed
  1489.         if (FrontWindow())
  1490.             EnableCommand(cClose);
  1491.  
  1492.         // changed documents can be saved, but only if the file is open for write
  1493.         if (     (pData->changed) && 
  1494.                 ((pData->isWritable) || (pData->dataRefNum == -1)) )
  1495.             EnableCommand(cSave);
  1496.         
  1497.         // objects with a print method can be printed and page setup-ed
  1498.         if (pData->pPrintPage)
  1499.             {
  1500.             EnableCommand(cPrint);
  1501.             EnableCommand(cPageSetup);
  1502.             EnableCommand(cPrintOneCopy);
  1503.             }
  1504.             
  1505.         // let object enable anything else that needs to be enabled
  1506.         if (pData->pAdjustMenus)
  1507.             (*(pData->pAdjustMenus)) (pWindow, pData);
  1508.         }
  1509.         
  1510.     // Now determine if any of the menus have changed state
  1511.     {
  1512.     Boolean gotToRedraw = false;
  1513.     
  1514.     for (whichMenu = mApple; whichMenu <= mLastMenu; ++whichMenu)
  1515.         {
  1516.         menu = GetMenuHandle(whichMenu);
  1517.     
  1518.         if (menu)        // because contents menu may not be around
  1519.             {
  1520.             // If any of the menu is enabled 
  1521.             if ((**menu).enableFlags != 0)
  1522.                 {
  1523.                 // Make sure to turn on the menu title 
  1524.                 (**menu).enableFlags |= 1;
  1525.                 }
  1526.                 
  1527.             /*     If this new state is different than the saved state, then the menu bar
  1528.                 will need to be redrawn */
  1529.             if (wasEnabled[mLastMenu - whichMenu] != ((**menu).enableFlags && 1))
  1530.                 {
  1531.                 gotToRedraw = true;
  1532.                 }
  1533.             }
  1534.         }
  1535.         
  1536.     // And if any titles have changed state, redraw them 
  1537.     if (gotToRedraw)
  1538.         DrawMenuBar();
  1539.     }
  1540.         
  1541. } // AdjustMenus
  1542.  
  1543. // --------------------------------------------------------------------------------------------------------------
  1544. // PRINTING UTILITY ROUTINES
  1545. // --------------------------------------------------------------------------------------------------------------
  1546. #pragma segment Printing
  1547.  
  1548. Boolean IsSomewhereInRectangle(gxRectangle *pContainer, gxRectangle *pShape)
  1549. /*
  1550.     Calculates this by saying the rectangle doesn't intersect at ALL,
  1551.     and then NOTs that expression.
  1552. */
  1553. {
  1554.     return
  1555.         (!(
  1556.         pShape->top > pContainer->bottom ||
  1557.         pShape->bottom < pContainer->top ||
  1558.         pShape->left > pContainer->right ||
  1559.         pShape->right < pContainer->left
  1560.         ));
  1561.         
  1562. } // IsSomewhereInRectangle
  1563.  
  1564. // --------------------------------------------------------------------------------------------------------------
  1565. #pragma segment Printing
  1566.     
  1567. static OSErr SimpleCatchShape(gxShape newShape, CatchRefCon * pRefCon)
  1568. {
  1569.     Boolean                addShape = false;
  1570.     gxGraphicsError        anErr;
  1571.  
  1572.     // did the user abort printing?
  1573.     anErr = GXGetJobError(pRefCon->theJob);
  1574.     if (anErr != noErr)
  1575.         return anErr;
  1576.         
  1577.     switch (GXGetShapeType(newShape))
  1578.         {
  1579.         
  1580.         // if we have a layout, turn off justification so that we can get
  1581.         // better looking morph effects, and also enable the default features
  1582.         // of the layout.  However, we can only do this if the layout is
  1583.         // in a script system where the translation wasn't done by a Print Action Hook.
  1584.         // For now, this means only smRoman styles.
  1585.         case gxLayoutType:
  1586.             if (pRefCon->doLayout)
  1587.                 {
  1588.                 Boolean            enableLayout = pRefCon->doLayout;
  1589.         
  1590.                 gxStyle            * theStyles;
  1591.                 long            styleCount, index;
  1592.                 
  1593.                 GXGetLayout(newShape, nil,
  1594.                             &styleCount, nil, nil,    // style runs
  1595.                             nil, nil, nil,    // run levels
  1596.                             nil, nil);
  1597.                 theStyles = (gxStyle*) NewPtr(sizeof(gxStyle)*styleCount);
  1598.                 if (theStyles)
  1599.                     {
  1600.                     GXGetLayout(newShape, nil,
  1601.                                 &styleCount, nil, theStyles,    // style runs
  1602.                                 nil, nil, nil,    // run levels
  1603.                                 nil, nil);
  1604.                     
  1605.                     enableLayout = true;
  1606.                     for (index = 0; index < styleCount; ++index)
  1607.                         {
  1608.                         gxFontScript theScript;
  1609.                         gxFontPlatform thePlatform = GXGetStyleEncoding(theStyles[index], &theScript, nil);
  1610.                         
  1611.                         if ((thePlatform != gxMacintoshPlatform) || (theScript != gxRomanScript))
  1612.                             enableLayout = false;
  1613.                         }
  1614.                             
  1615.                     if (enableLayout)
  1616.                         for (index = 0; index < styleCount; ++index)
  1617.                             {
  1618.                             gxRunControls theControls;
  1619.                             
  1620.                             // re-enable run control features
  1621.                             GXGetStyleRunControls(theStyles[index], &theControls);
  1622.                             theControls.flags = 0;
  1623.                             theControls.track = 0;
  1624.                             theControls.hangingInhibitFactor = 0;
  1625.                             theControls.kerningInhibitFactor = 0;
  1626.                             GXSetStyleRunControls(theStyles[index], &theControls);
  1627.                             
  1628.                             // and turn back on default features
  1629.                             GXSetStyleRunFeatures(theStyles[index], 0, nil);
  1630.                             }
  1631.                         
  1632.                     DisposePtr((Ptr) theStyles);
  1633.                     }
  1634.         
  1635.                 if ( (enableLayout) && (GetSysDirection() == 0) )
  1636.                     {
  1637.                     // turn off justification
  1638.                     {
  1639.                     gxLayoutOptions    layoutOptions;
  1640.             
  1641.                     // get the current layout options
  1642.                     layoutOptions.baselineRec = nil;
  1643.                     GXGetLayout(newShape, nil,
  1644.                                 nil, nil, nil,    // style runs
  1645.                                 nil, nil, nil,    // run levels
  1646.                                 &layoutOptions, nil);
  1647.                         
  1648.                     // setting width to zero allows lines to float, but causes multi-styled
  1649.                     // lines (separate layouts from the translation process) to run into
  1650.                     // one another, so we can't do that
  1651.                     //layoutOptions.width = 0;
  1652.                     layoutOptions.just = gxNoJustification;
  1653.                     GXSetLayout(newShape, 
  1654.                                 0, nil, nil,    // text runs
  1655.                                 0, nil, nil,    // style runs
  1656.                                 0, nil, nil, // run levels
  1657.                                 &layoutOptions, nil);
  1658.                     }
  1659.                     
  1660.                     // un-clip the shape left and right so we can see things like hanging puncs.
  1661.                         {
  1662.                         gxShape            newClip;
  1663.                         gxRectangle        bounds;
  1664.                         
  1665.                         newClip = GXGetShapeClip(newShape);
  1666.                         GXGetShapeLocalBounds(newClip, &bounds);
  1667.                         GXDisposeShape(newClip);
  1668.                         
  1669.                         bounds.left     = gxNegativeInfinity;
  1670.                         bounds.right     = gxPositiveInfinity;
  1671.                         newClip = GXNewRectangle(&bounds);
  1672.                         GXSetShapeClip(newShape, newClip);
  1673.                         GXDisposeShape(newClip);
  1674.                         }
  1675.                     }
  1676.                 
  1677.                 }
  1678.  
  1679.             // if we aren't forming layouts, we'll go ahead and check for this being on
  1680.             // the shape.  But for pure text, we'll just always add the text.  TextEdit
  1681.             // is pretty good about pre-clipping for us.
  1682.             if (!pRefCon->doLayout)
  1683.                 {
  1684.                 gxRectangle    bounds;
  1685.                 
  1686.                 GXGetShapeLocalBounds(newShape, &bounds);
  1687.                 if (IsSomewhereInRectangle(&pRefCon->thePageRectangle, &bounds))
  1688.                     addShape = true;
  1689.                 }
  1690.             else
  1691.                 addShape = true;
  1692.             break;
  1693.         
  1694.         // never add these for text case
  1695.         case gxRectangleType:
  1696.             if (!pRefCon->doLayout)
  1697.                 addShape = true;
  1698.             break;
  1699.             
  1700.         // always add these shapes if we see any because we don't know how to filter them
  1701.         case gxEmptyType:
  1702.         case gxFullType:
  1703.         case gxPictureType:
  1704.             addShape = true;
  1705.             break;
  1706.             
  1707.         default:
  1708.             {
  1709.             gxRectangle    bounds;
  1710.             
  1711.             GXGetShapeLocalBounds(newShape, &bounds);
  1712.             if (IsSomewhereInRectangle(&pRefCon->thePageRectangle, &bounds))
  1713.                 addShape = true;
  1714.             }
  1715.             break;
  1716.             
  1717.         } // switch
  1718.         
  1719.     if (addShape)
  1720.         GXSetPictureParts(pRefCon->thePage, 0, 0, 1, &newShape, nil, nil, nil);    /* Add shape */
  1721.     
  1722.     GXGetGraphicsError(&anErr);
  1723.     
  1724.     if (anErr == noErr)
  1725.         {
  1726.         GXIdleJob(pRefCon->theJob);
  1727.         anErr = GXGetJobError(pRefCon->theJob);
  1728.         }
  1729.         
  1730.     return anErr;
  1731.     
  1732. } // SimpleCatchShape
  1733.  
  1734. #if GENERATINGCFM
  1735.     static RoutineDescriptor gSimpleCatchShapeRD = BUILD_ROUTINE_DESCRIPTOR(uppgxShapeSpoolProcInfo, SimpleCatchShape);
  1736.     static gxShapeSpoolUPP gSimpleCatchShape = &gSimpleCatchShapeRD;
  1737. #else
  1738.     static gxShapeSpoolUPP gSimpleCatchShape = NewgxShapeSpoolProc(SimpleCatchShape);
  1739. #endif
  1740.  
  1741. // --------------------------------------------------------------------------------------------------------------
  1742. #pragma segment Printing
  1743.  
  1744. static OSErr CompleteSpoolFileMessage( gxSpoolFile theSpoolFile )
  1745. {
  1746.     OSErr            anErr         = noErr;
  1747.     Handle            hIcon;
  1748.     short            sourceIcon     = 0;
  1749.     short            docIcon     = 132;
  1750.     WindowDataPtr    pData         = GXGetJobRefCon(GXGetJob());
  1751.     
  1752.     // for some file types, we can supply a nicer icon for the Finder to display
  1753.     // within the queue when this document is printing
  1754.     switch (pData->originalFileType)
  1755.         {
  1756.         case 'TEXT':
  1757.         case 'sEXT':
  1758.             sourceIcon = kTextIcon;
  1759.             break;
  1760.                         
  1761.         case 'ttro':
  1762.             sourceIcon = kReadOnlyIcon;
  1763.             break;
  1764.  
  1765.         case 'PICT':
  1766.             sourceIcon = kPICTIcon;
  1767.             break;
  1768.         }
  1769.         
  1770.     if (sourceIcon != 0)
  1771.         {
  1772.         hIcon = GetResource('ICN#', sourceIcon);
  1773.         if (hIcon != nil) 
  1774.             {
  1775.             HNoPurge(hIcon);
  1776.             DetachResource(hIcon);
  1777.             anErr = Send_GXSpoolResource (theSpoolFile, hIcon, 'ICN#', docIcon);
  1778.             }
  1779.         nrequire(anErr, SpoolOneBit);
  1780.         
  1781.         hIcon = GetResource('icl4', sourceIcon);
  1782.         if (hIcon != nil) 
  1783.             {
  1784.             HNoPurge(hIcon);
  1785.             DetachResource(hIcon);
  1786.             anErr = Send_GXSpoolResource (theSpoolFile, hIcon, 'icl4', docIcon);
  1787.             }
  1788.         nrequire(anErr, SpoolFourBit);
  1789.     
  1790.         hIcon = GetResource('icl8', sourceIcon);
  1791.         if (hIcon != nil) 
  1792.             {
  1793.             HNoPurge(hIcon);
  1794.             DetachResource(hIcon);
  1795.             anErr = Send_GXSpoolResource (theSpoolFile, hIcon, 'icl8', docIcon);
  1796.             }
  1797.         nrequire(anErr, SpoolEightBit);
  1798.         }
  1799.     
  1800.     anErr = Forward_GXCompleteSpoolFile( theSpoolFile );
  1801.     
  1802. // FALL THROUGH EXCEPTION HANDLING
  1803. SpoolOneBit:
  1804. SpoolFourBit:
  1805. SpoolEightBit:
  1806.  
  1807.     return anErr;
  1808.     
  1809. } // CompleteSpoolFileMessage
  1810.  
  1811. #if GENERATINGCFM
  1812.     static RoutineDescriptor gCompleteSpoolFileMessageRD = BUILD_ROUTINE_DESCRIPTOR(uppGXCompleteSpoolFileProcInfo, CompleteSpoolFileMessage);
  1813.     static GXCompleteSpoolFileUPP gCompleteSpoolFileMessage = &gCompleteSpoolFileMessageRD;
  1814. #else
  1815.     static GXCompleteSpoolFileUPP gCompleteSpoolFileMessage = NewGXCompleteSpoolFileProc(CompleteSpoolFileMessage);
  1816. #endif
  1817.  
  1818. // --------------------------------------------------------------------------------------------------------------
  1819. #pragma segment Printing
  1820.  
  1821. static OSErr PrintingEventMessage(EventRecord *event, Boolean filter)
  1822. {
  1823.     OSErr     anErr = noErr;
  1824.     GrafPtr    curPort;
  1825.     
  1826.     GetPort(&curPort);
  1827.     if (filter == false) 
  1828.         {
  1829.         switch ( event->what ) 
  1830.             {
  1831.             case mouseDown:
  1832.             case keyDown:
  1833.             case autoKey:
  1834.                 break;
  1835.                 
  1836.             case activateEvt:
  1837.             case updateEvt:
  1838.             default:
  1839.                 HandleEvent(event);
  1840.                 break;
  1841.             }
  1842.         }
  1843.     
  1844.     anErr = Forward_GXPrintingEvent(event, filter);
  1845.     SetPort(curPort);
  1846.     
  1847.     return anErr;
  1848.     
  1849. } // PrintingEventMessage
  1850.  
  1851. #if GENERATINGCFM
  1852.     static RoutineDescriptor gPrintingEventMessageRD = BUILD_ROUTINE_DESCRIPTOR(uppGXPrintingEventProcInfo, PrintingEventMessage);
  1853.     static GXPrintingEventUPP gPrintingEventMessage = &gPrintingEventMessageRD;
  1854. #else
  1855.     static GXPrintingEventUPP gPrintingEventMessage = NewGXPrintingEventProc(PrintingEventMessage);
  1856. #endif
  1857.  
  1858.  
  1859.  
  1860. // --------------------------------------------------------------------------------------------------------------
  1861. // FILE UTILITY ROUTINES
  1862. // --------------------------------------------------------------------------------------------------------------
  1863. #pragma segment Main
  1864.  
  1865. static Boolean BringToFrontIfOpen(FSSpecPtr pSpec)
  1866. {
  1867.     WindowRef        pWindow;
  1868.     
  1869.     pWindow = FrontWindow();
  1870.     while (pWindow)
  1871.         {
  1872.         WindowDataPtr pData = GetWindowInfo(pWindow);
  1873.         
  1874.         if (
  1875.             (pData) &&
  1876.             (pData->fileSpec.vRefNum == pSpec->vRefNum) &&
  1877.             (pData->fileSpec.parID == pSpec->parID) &&
  1878.             EqualString(pData->fileSpec.name, pSpec->name, false, false)
  1879.             )
  1880.             {
  1881.             SelectWindow(pWindow);
  1882.             return true;
  1883.             }
  1884.             
  1885.         pWindow = GetNextWindow(pWindow);
  1886.         }
  1887.         
  1888.     return false;
  1889.     
  1890. } // BringToFrontIfOpen
  1891.  
  1892. // --------------------------------------------------------------------------------------------------------------
  1893. #pragma segment Main
  1894.  
  1895. static Boolean BringToFrontIfExists(ResType windowKind)
  1896. {
  1897.     WindowRef        pWindow;
  1898.     
  1899.     pWindow = FrontWindow();
  1900.     while (pWindow)
  1901.         {
  1902.         WindowDataPtr pData = GetWindowInfo(pWindow);
  1903.         
  1904.         if ((pData) && (pData->windowKind == windowKind))
  1905.             {
  1906.             SelectWindow(pWindow);
  1907.             return true;
  1908.             }
  1909.             
  1910.         pWindow = GetNextWindow(pWindow);
  1911.         }
  1912.         
  1913.     return false;
  1914.     
  1915. } // BringToFrontIfExists
  1916.  
  1917. // --------------------------------------------------------------------------------------------------------------
  1918. // MAIN SIMPLETEXT ROUTINES
  1919. // --------------------------------------------------------------------------------------------------------------
  1920. #pragma segment Main
  1921.  
  1922. static OSErr MakeNewWindow(ResType windowKind, FSSpecPtr fileSpec, OSType fileType, Boolean *pWasAlreadyOpen)
  1923. {
  1924.     OSErr                anErr = fnfErr;
  1925.     PreflightRecord        thePreflight;
  1926.     PreflightWindowProc    pPreflight = nil;
  1927.     WindowRef            pWindow;
  1928.     WindowDataPtr        pData;
  1929.     
  1930.     // require a certain amount of RAM free before we allow the new window to be created
  1931.     if (FreeMem() < kRAMNeededForNew)
  1932.         anErr = memFullErr;
  1933.         
  1934.     // <50> if we already have a document open from this file, bring the window to the
  1935.     // front and return with no error
  1936.     if ( (fileSpec) && (fileType != 'sEXT') && (BringToFrontIfOpen(fileSpec)) )
  1937.         {
  1938.         if (pWasAlreadyOpen)
  1939.             *pWasAlreadyOpen = true;
  1940.         anErr = noErr;
  1941.         return(anErr);
  1942.         }
  1943.     if (pWasAlreadyOpen)
  1944.         *pWasAlreadyOpen = false;
  1945.     if (anErr != fnfErr)
  1946.         {
  1947.         nrequire(anErr, SanityCheckFailed);
  1948.         }
  1949.         
  1950.     // initialize our behavior
  1951.     thePreflight.continueWithOpen     = true;
  1952.     thePreflight.resourceID         = kDefaultWindowID;
  1953.     thePreflight.wantHScroll         = false;
  1954.     thePreflight.wantVScroll         = false;
  1955.     thePreflight.storageSize         = sizeof(WindowDataRecord);
  1956.     thePreflight.makeProcPtr         = nil;
  1957.     thePreflight.openKind            = fsRdPerm;
  1958.     thePreflight.needResFork        = false;
  1959.     thePreflight.doZoom                = false;
  1960.     thePreflight.fileType            = fileType;
  1961.     
  1962.     switch (windowKind)
  1963.         {
  1964.         case kAboutWindow:
  1965.             pPreflight = AboutPreflightWindow;
  1966.             break;
  1967.  
  1968.         case kPICTWindow:
  1969.             pPreflight = PICTPreflightWindow;
  1970.             break;
  1971.  
  1972.         case kMovieWindow:
  1973.             pPreflight = MoviePreflightWindow;
  1974.             break;
  1975.  
  1976.         case kClipboardWindow:
  1977.             pPreflight = ClipboardPreflightWindow;
  1978.             break;
  1979.  
  1980.         case kTextWindow:
  1981.             pPreflight = TextPreflightWindow;
  1982.             break;
  1983.  
  1984.         case kGXWindow:
  1985.             pPreflight = GXPreflightWindow;
  1986.             break;
  1987.  
  1988.         case kThreeDWindow:
  1989.             pPreflight = ThreeDPreflightWindow;
  1990.             break;
  1991.         }
  1992.     
  1993.     // preflight the window    
  1994.     if (pPreflight)
  1995.         anErr = (*pPreflight) (&thePreflight);
  1996.     nrequire(anErr, PreflightFailed);
  1997.     
  1998.     if (thePreflight.continueWithOpen)
  1999.         {
  2000.         // allocate a place for the window
  2001.         pData = (WindowDataPtr)NewPtrClear(thePreflight.storageSize);
  2002.         anErr = MemError();
  2003.         nrequire(anErr, FailedToAllocateWindow);
  2004.         
  2005.         // then actually create the window
  2006.         if (gMachineInfo.theEnvirons.hasColorQD)
  2007.             pWindow = (WindowRef)GetNewCWindow(thePreflight.resourceID, pData, (WindowPtr)-1);
  2008.         else
  2009.             pWindow = (WindowRef)GetNewWindow(thePreflight.resourceID, pData, (WindowPtr)-1);
  2010.         if (!pWindow) anErr = memFullErr;
  2011.         nrequire(anErr, NewWindowFailed);
  2012.         SetWRefCon(pWindow, (long) pData);
  2013.                 
  2014.         // zoom the rectangle to big size on this monitor 
  2015.         // based upon which scroll bars they want
  2016.         {
  2017.         Rect    rect = GetWindowPort(pWindow)->portRect;
  2018.         Rect    bigRect;
  2019.         
  2020.         if (gMachineInfo.theEnvirons.hasColorQD)
  2021.             bigRect = (**GetMainDevice()).gdRect;
  2022.         else
  2023.             bigRect = qd.screenBits.bounds;
  2024.         bigRect.top += GetMBarHeight() * 2;
  2025.         bigRect.left += 4;
  2026.         bigRect.bottom -= 4;
  2027.         bigRect.right -= 65;
  2028.         
  2029.         SetPort((GrafPtr) GetWindowPort(pWindow));
  2030.         LocalToGlobal(&TopLeft(rect));
  2031.         LocalToGlobal(&BotRight(rect));
  2032.         
  2033.         if ( (thePreflight.wantHScroll) || (thePreflight.doZoom) )
  2034.             {
  2035. //            rect.left = bigRect.left;
  2036.             rect.right = bigRect.right;
  2037.             }
  2038.             
  2039.         if ( (thePreflight.wantVScroll) || (thePreflight.doZoom) )
  2040.             {
  2041. //            rect.top = bigRect.top;
  2042.             rect.bottom = bigRect.bottom;
  2043.             }
  2044.         
  2045.         MoveWindow(pWindow, rect.left, rect.top, false);
  2046.         SizeWindow(pWindow, rect.right - rect.left, rect.bottom - rect.top, false);
  2047.         }
  2048.         
  2049.         // fill in the default contents of the window
  2050.         pData->windowKind         = windowKind;
  2051.         pData->originalFileType    = fileType;
  2052.         pData->pMakeWindow         = (MakeWindowProc)thePreflight.makeProcPtr;
  2053.         pData->resRefNum        = -1;
  2054.         pData->dataRefNum        = -1;
  2055.         pData->contentRect         = GetWindowPort(pWindow)->portRect;
  2056.         
  2057.         // make the scroll bars
  2058.         {
  2059.         Rect    controlRect;
  2060.         
  2061.         if (thePreflight.wantHScroll)
  2062.             {
  2063.             pData->contentRect.bottom -= kScrollBarSize;
  2064.             controlRect = GetWindowPort(pWindow)->portRect;
  2065.             controlRect.top = controlRect.bottom - 16;
  2066.             if (thePreflight.wantVScroll)
  2067.                 controlRect.right -= kGrowScrollAdjust;
  2068.             OffsetRect(&controlRect, -1, 1);
  2069.             pData->hScroll = NewControl(pWindow, &controlRect, "\p", true, 0, 0, 0, scrollBarProc, 0);
  2070.             }
  2071.         if (thePreflight.wantVScroll)
  2072.             {
  2073.             pData->contentRect.right -= kScrollBarSize;
  2074.             controlRect = GetWindowPort(pWindow)->portRect;
  2075.             controlRect.left = controlRect.right - 16;
  2076.             if (thePreflight.wantVScroll)
  2077.                 controlRect.bottom -= kGrowScrollAdjust;
  2078.             OffsetRect(&controlRect, 1, -1);
  2079.             pData->vScroll = NewControl(pWindow, &controlRect, "\p", true, 0, 0, 0, scrollBarProc, 0);
  2080.             }
  2081.         }
  2082.  
  2083.         // got a name?  Open the file        
  2084.         if (fileSpec)
  2085.             {
  2086.             anErr = FSpOpenDF(fileSpec, thePreflight.openKind, &pData->dataRefNum);
  2087.             if     ( 
  2088.                     ((anErr == afpAccessDenied) || (anErr == opWrErr) || (anErr == permErr) ) && 
  2089.                     (thePreflight.openKind != fsRdPerm)
  2090.                 )
  2091.                 {
  2092.                 thePreflight.openKind = fsRdPerm;
  2093.                 pData->isWritable = false;
  2094.                 anErr = FSpOpenDF(fileSpec, thePreflight.openKind, &pData->dataRefNum);
  2095.                 }
  2096.             else
  2097.                 pData->isWritable = true;
  2098.             nrequire(anErr, FailedToOpenFile);
  2099.  
  2100.             // okay not to find a resource fork, because some don't have them                
  2101.             pData->resRefNum = FSpOpenResFile(fileSpec, thePreflight.openKind);
  2102.             
  2103.             // save the file spec in case someone is interested
  2104.             pData->fileSpec = *fileSpec;
  2105.             }
  2106.             
  2107.         if (pData->pMakeWindow)
  2108.             {
  2109.             Rect oldContent = pData->contentRect;
  2110.             anErr = (*(pData->pMakeWindow)) (pWindow, pData);
  2111.             if (!EqualRect(&oldContent, &pData->contentRect))
  2112.                 {
  2113.                 SizeWindow(pWindow, 
  2114.                         pData->contentRect.right  + (pData->vScroll != 0) * kScrollBarSize,
  2115.                         pData->contentRect.bottom + (pData->hScroll != 0) * kScrollBarSize,
  2116.                         false);
  2117.                 }
  2118.             }
  2119.         nrequire(anErr, FailedMakeWindow);
  2120.  
  2121.         // got a name?  Use it as the window title
  2122.         if ( (fileSpec) && (!pData->openAsNew) )
  2123.             SetWTitle(pWindow, fileSpec->name);
  2124.         else
  2125.             {
  2126.             if ((gMachineInfo.documentCount == 1) && (pData->windowKind == kTextWindow))
  2127.                 {
  2128.                 Str255 tempString;
  2129.         
  2130.                 GetIndString(tempString, kMiscStrings, iFirstNewDocumentTitle);    // get the "untitled" string (no number)
  2131.                 SetWTitle(pWindow, tempString);
  2132.                 }
  2133.             else
  2134.                 {
  2135.                 Str255    tempString;
  2136.                 Str32    numString;
  2137.     
  2138.                 GetWTitle(pWindow, tempString);
  2139.                 NumToString(gMachineInfo.documentCount, numString);
  2140.                 (void) ZeroStringSub(tempString, numString);
  2141.                 SetWTitle(pWindow, tempString);
  2142.                 }
  2143.  
  2144.             if (pData->bumpUntitledCount)
  2145.                 gMachineInfo.documentCount++;    // bump count if appropriate for this kind of document
  2146.             }
  2147.  
  2148.         // Make sure the scroll bars are reasonable in size, and move if they must
  2149.         AdjustScrollBars(pWindow, true, true, nil);
  2150.  
  2151.         // finally, if all goes well, we can see the window itself!
  2152.         ShowWindow(pWindow);
  2153.         }
  2154.  
  2155.     return noErr;
  2156.  
  2157. // EXCEPTION HANDLING
  2158. FailedMakeWindow:
  2159.     if (pData->resRefNum != -1)
  2160.         CloseResFile(pData->resRefNum);
  2161.     if (pData->dataRefNum != -1)
  2162.         FSClose(pData->dataRefNum);
  2163.     
  2164. FailedToOpenFile:
  2165.     CloseWindow(pWindow);
  2166.     
  2167. NewWindowFailed:
  2168.     DisposePtr((Ptr)pData);
  2169.     
  2170. FailedToAllocateWindow:
  2171. PreflightFailed:
  2172. SanityCheckFailed:
  2173.     return anErr;
  2174.     
  2175. } // MakeNewWindow
  2176.  
  2177. // --------------------------------------------------------------------------------------------------------------
  2178. #pragma segment Main
  2179.  
  2180.  
  2181. #define kVisualDelay 8
  2182.  
  2183. static pascal Boolean SaveChangesFilter(DialogRef theDialog, EventRecord *theEvent, short *itemHit)
  2184. {
  2185.     if (StdFilterProc(theDialog, theEvent, itemHit))
  2186.         return true;
  2187.  
  2188.     if (theEvent->what == updateEvt)
  2189.         {
  2190.         HandleEvent(theEvent);
  2191.         }
  2192.  
  2193.     if (theEvent->what == keyDown)
  2194.         {
  2195.         StringPtr keyEquivs = *GetString(kSaveChangesWindowID);
  2196.         unsigned char theKey = theEvent->message & charCodeMask;
  2197.  
  2198.         if (keyEquivs && (theKey == keyEquivs[1] || theKey == keyEquivs[2]))
  2199.             {
  2200.             short itemType;
  2201.             Rect theRect;
  2202.             ControlRef theControl;
  2203.             long finalTicks;
  2204.  
  2205.             GetDialogItem(theDialog, dontSaveChanges, &itemType, (Handle *) &theControl, &theRect);
  2206.             HiliteControl(theControl, kControlButtonPart);
  2207.             Delay(kVisualDelay, &finalTicks);
  2208.             HiliteControl(theControl, 0);
  2209.  
  2210.             *itemHit = dontSaveChanges;
  2211.             return true;
  2212.             }
  2213.         }
  2214.  
  2215.     return false;
  2216. }
  2217.  
  2218.  
  2219. static OSErr DoCloseWindow(WindowRef pWindow, Boolean quitting)
  2220. {
  2221.     OSErr            anErr = noErr;
  2222.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  2223.     
  2224.     if ( (pData) && (pData->changed) )
  2225.         {
  2226.         short         hit;
  2227.         Str255        wTitle;
  2228.         DialogRef    dPtr;
  2229.         
  2230.         GetWTitle(pWindow, wTitle);
  2231.         SetCursor(&qd.arrow);
  2232.         ParamText(wTitle, "\p", "\p", "\p");
  2233.         
  2234.         if (gMachineInfo.haveNavigationServices)
  2235.         {
  2236.             hit = ConfirmSaveDialog(wTitle, quitting, MyEventProc);
  2237.         }
  2238.         else
  2239.         {
  2240.             hit = cancel;
  2241.             dPtr = GetNewDialog(kSaveChangesWindowID, nil, (WindowRef)-1);
  2242.             if (dPtr)
  2243.                 {
  2244.                 SetDialogDefaultItem(dPtr, ok);
  2245.                 SetDialogCancelItem (dPtr, cancel);
  2246.                 BeginMovableModal();
  2247.                 do
  2248.                     {
  2249.                     MovableModalDialog(SaveChangesFilter, &hit);
  2250.                     } while ((hit != dontSaveChanges) && (hit != ok) && (hit != cancel));
  2251.                     
  2252.                 DisposeDialog(dPtr);
  2253.                 EndMovableModal();
  2254.                 }
  2255.         }
  2256.     
  2257.         switch (hit)
  2258.             {
  2259.             case ok:
  2260.                 anErr = DoCommand(pWindow, cSave, 0);
  2261.                 if (anErr == eUserCanceled)        // redundant?
  2262.                     gAllDone = false;
  2263.                 break;
  2264.                 
  2265.             case cancel:
  2266.                 anErr = eUserCanceled;
  2267.                 gAllDone = false;
  2268.                 break;
  2269.                 
  2270.             case dontSaveChanges:
  2271.                 // don't save, so just close it
  2272.                 break;
  2273.             }
  2274.         }
  2275.  
  2276.     if (anErr == noErr)
  2277.         {
  2278.         if ( (pData) && (pData->pCloseWindow) )
  2279.             {
  2280.             // let the object close the window if it wishes to
  2281.             anErr = (*(pData->pCloseWindow)) (pWindow, pData);
  2282.             }
  2283.  
  2284.         // otherwise we close it the default way
  2285.         if (anErr == noErr)
  2286.             {
  2287.             if (pData)
  2288.                 {
  2289.                 CloseWindow(pWindow);
  2290.     
  2291.                 if (pData->hPrint)
  2292.                     {
  2293.                     if (gMachineInfo.haveGX)
  2294.                         GXDisposeJob( pData->hPrint);
  2295.                     else
  2296.                         DisposeHandle((Handle) pData->hPrint);
  2297.                     }
  2298.                     
  2299.                 if (pData->resRefNum != -1)
  2300.                     CloseResFile(pData->resRefNum);
  2301.                 if (pData->dataRefNum != -1)
  2302.                     FSClose(pData->dataRefNum);
  2303.                 DisposePtr((Ptr) pData);
  2304.                 }
  2305.             }
  2306.         }
  2307.  
  2308.     // If we closed the last window, clean up
  2309.     if (FrontWindow() == nil)
  2310.         {
  2311.         AdjustMenus(nil, true, false);
  2312.         gMachineInfo.documentCount = 1;        // back to "untitled"
  2313.         }
  2314.     
  2315.     return anErr;
  2316.     
  2317. } // DoCloseWindow
  2318.  
  2319. #undef dontSaveChanges
  2320.  
  2321. // --------------------------------------------------------------------------------------------------------------
  2322. #pragma segment Main
  2323.  
  2324. static OSErr    DetermineWindowTypeOrOpen(
  2325.     FSSpecPtr theSpec, OSType theType,                 // optional input params -- file to open
  2326.     OSType *returnedTypeList, short * pNumTypes,    // optional input params -- returns list of files
  2327.     Boolean *pWasAlreadyOpen)                        // optional input params -- was file already open
  2328. {
  2329.     OSErr        anErr = noErr;
  2330.     OSType        typeList[20];
  2331.     OSType        docList[20];
  2332.     short        numTypes;
  2333.  
  2334.     // use local copies if the input params are nil    
  2335.     if (returnedTypeList == nil)
  2336.         returnedTypeList = &typeList[0];
  2337.     if (pNumTypes == nil)
  2338.         pNumTypes = &numTypes;
  2339.     *pNumTypes = 0;
  2340.     
  2341.     // Load up all of the file types we know how to handle
  2342.     AboutGetFileTypes(returnedTypeList, docList, pNumTypes);
  2343.     PICTGetFileTypes(returnedTypeList, docList, pNumTypes);
  2344.     MovieGetFileTypes(returnedTypeList, docList, pNumTypes);
  2345.     ClipboardGetFileTypes(returnedTypeList, docList, pNumTypes);
  2346.     TextGetFileTypes(returnedTypeList, docList, pNumTypes);
  2347.     GXGetFileTypes(returnedTypeList, docList, pNumTypes);
  2348.     ThreeDGetFileTypes(returnedTypeList, docList, pNumTypes);
  2349.  
  2350.     if (theSpec != nil)
  2351.         {
  2352.         short         index;
  2353.         OSType        windowType = '????';
  2354.  
  2355.         for (index = 0; index < (*pNumTypes); ++index)
  2356.             if (theType == returnedTypeList[index])
  2357.                 windowType = docList[index];
  2358.                 
  2359.         if (windowType != '????')
  2360.             {
  2361.             
  2362.             if ( (theType == 'TEXT') || (theType == 'sEXT') )
  2363.                 {
  2364.                 FInfo    theInfo;
  2365.                 
  2366.                 FSpGetFInfo(theSpec, &theInfo);
  2367.                 if ((theInfo.fdFlags & kIsStationary) != 0)
  2368.                     theType = 'sEXT';
  2369.                 else
  2370.                     theType = 'TEXT';
  2371.                 }
  2372.                 
  2373.             anErr = MakeNewWindow(windowType, theSpec, theType, pWasAlreadyOpen);
  2374.             }
  2375.         else
  2376.             anErr = eDocumentWrongKind;
  2377.         }
  2378.         
  2379.         
  2380.     return anErr;
  2381.     
  2382. } // DetermineWindowTypeOrOpen
  2383.  
  2384. // --------------------------------------------------------------------------------------------------------------
  2385. #pragma segment Main
  2386.  
  2387. // Handle update/activate events behind Standard File
  2388. static pascal Boolean OpenDialogFilter(DialogRef theDialog, EventRecord *theEvent,
  2389.                                       short *itemHit, void *myDataPtr)
  2390. {
  2391.     #pragma unused(myDataPtr)
  2392.  
  2393.     // Pass updates through (Activates are tricky...was mucking with Apple menu & thereby
  2394.     // drastically changing how the system handles the menu bar during our alert)
  2395.     if ( (theEvent->what == updateEvt) && (theEvent->message != (long)theDialog) )
  2396.         HandleEvent(theEvent);
  2397.  
  2398.     if (StdFilterProc(theDialog, theEvent, itemHit))
  2399.         return(true);
  2400.  
  2401.     return(false);
  2402.  
  2403. } // OpenDialogFilter
  2404.  
  2405. #if GENERATINGCFM
  2406.     static RoutineDescriptor gOpenDialogFilterRD = BUILD_ROUTINE_DESCRIPTOR(uppModalFilterYDProcInfo, OpenDialogFilter);
  2407.     static ModalFilterYDUPP gOpenDialogFilter = &gOpenDialogFilterRD;
  2408. #else
  2409.     static ModalFilterYDUPP gOpenDialogFilter = NewModalFilterYDProc(OpenDialogFilter);
  2410. #endif
  2411.  
  2412.  
  2413. static OSErr DoOpenWindow(void)
  2414. {
  2415.     OSErr                anErr = noErr;
  2416.     short                numTypes;
  2417.     OSType                typeList[20];
  2418.     Point                thePoint = { -1, -1 };
  2419.     FSSpec                fileSpec;
  2420.     OSType                fileType;
  2421.     Boolean                fileSelected;
  2422.     
  2423.     DetermineWindowTypeOrOpen(nil, '????', &typeList[0], &numTypes, nil);
  2424.     
  2425.     if (gMachineInfo.haveNavigationServices)
  2426.         {
  2427.         #if qSingleSelectionOnly
  2428.             fileSelected = OpenFileDialog('ttxt', numTypes, typeList, MyEventProc, &fileSpec, &fileType) == noErr;
  2429.         #else
  2430.             // Open as many documents as the user wishes thruogh Appleevents
  2431.             OpenFileDialog('ttxt', numTypes, typeList, MyEventProc, NULL, NULL);
  2432.             fileSelected = false;
  2433.         #endif
  2434.         }
  2435.     else 
  2436.         {
  2437.         StandardFileReply    sfReply;
  2438.  
  2439.         if (gMachineInfo.haveQuickTime)
  2440.             {
  2441.             CustomGetFilePreview(nil, numTypes, typeList, &sfReply, 0, thePoint, nil, gOpenDialogFilter, nil, nil, nil);
  2442.             }
  2443.         else
  2444.             {
  2445.             CustomGetFile(nil, numTypes, typeList, &sfReply, 0, thePoint, nil, gOpenDialogFilter, nil, nil, nil);
  2446.             }
  2447.             
  2448.         fileSelected= sfReply.sfGood;
  2449.         fileSpec    = sfReply.sfFile;
  2450.         fileType    = sfReply.sfType;
  2451.         }
  2452.     
  2453.     if (fileSelected)
  2454.         {
  2455.         SetWatchCursor();
  2456.         
  2457.         anErr = DetermineWindowTypeOrOpen(&fileSpec, fileType, &typeList[0], &numTypes, nil);
  2458.  
  2459.         SetCursor(&qd.arrow);
  2460.         }
  2461.         
  2462.     return anErr;
  2463.     
  2464. } // DoOpenWindow
  2465.  
  2466. // --------------------------------------------------------------------------------------------------------------
  2467. #pragma segment Main
  2468.  
  2469. static OSErr DoUpdateWindow(WindowRef pWindow)
  2470. {
  2471.     OSErr            anErr = noErr;
  2472.     WindowDataPtr    pData = GetWindowInfo(pWindow);
  2473.     GrafPtr            curPort;
  2474.     
  2475.     // only handle updates for windows we know about
  2476.     if (pData)
  2477.         {
  2478.         GetPort(&curPort);
  2479.         SetPort((GrafPtr)GetWindowPort(pWindow));
  2480.         BeginUpdate(pWindow);
  2481.                     
  2482.         if (pData->pUpdateWindow)
  2483.             anErr = (*(pData->pUpdateWindow)) (pWindow, pData);
  2484.     
  2485.         EndUpdate(pWindow);
  2486.         SetPort(curPort);
  2487.         }
  2488.     
  2489.     return anErr;
  2490.     
  2491. } // DoUpdateWindow
  2492.  
  2493. // --------------------------------------------------------------------------------------------------------------
  2494. #pragma segment Main
  2495.  
  2496. OSErr DoScrollContent(WindowRef pWindow, WindowDataPtr pData, short deltaH, short deltaV)
  2497. {
  2498.     OSErr    anErr = noErr;
  2499.     
  2500.     if ((deltaH) || (deltaV))
  2501.         {
  2502.         // if we have a balloon displayed, update before scrolling anything
  2503.         if (HMIsBalloon())
  2504.             DoUpdateWindow(pWindow);
  2505.         
  2506.         if ((pData) && (pData->pScrollContent))
  2507.             anErr = (*(pData->pScrollContent)) (pWindow, pData, deltaH, deltaV);
  2508.             
  2509.         if (anErr == noErr)
  2510.             {
  2511.             RgnHandle    invalidRgn = NewRgn();
  2512.             
  2513.             ScrollRect(&pData->contentRect, deltaH, deltaV, invalidRgn);
  2514.             InvalRgn(invalidRgn);
  2515.             DisposeRgn(invalidRgn);
  2516.     
  2517.             DoUpdateWindow(pWindow);
  2518.             }
  2519.         }
  2520.     
  2521.     return anErr;
  2522.     
  2523. } // DoScrollContent
  2524.  
  2525. // --------------------------------------------------------------------------------------------------------------
  2526. // BEGIN SCROLL ACTION PROCS
  2527. // --------------------------------------------------------------------------------------------------------------
  2528. #pragma segment Main
  2529.  
  2530. void SetControlAndClipAmount(ControlRef control, short * amount)
  2531. {
  2532.     short        value, max;
  2533.     
  2534.     value = GetControlValue(control);    /* get current value */
  2535.     max = GetControlMaximum(control);        /* and maximum value */
  2536.     *amount = value - *amount;
  2537.     if ( *amount < 0 )
  2538.         *amount = 0;
  2539.     else
  2540.         {
  2541.         if ( *amount > max )
  2542.             *amount = max;
  2543.         }
  2544.     SetControlValue(control, *amount);
  2545.     *amount = value - *amount;        /* calculate the real change */
  2546.     
  2547. } // SetControlAndClipAmount
  2548.  
  2549. // --------------------------------------------------------------------------------------------------------------
  2550. static pascal void VActionProc(ControlRef control, short part)
  2551. {
  2552.     if (part != 0)
  2553.         {
  2554.         WindowRef        pWindow = (**control).contrlOwner;
  2555.         WindowDataPtr     pData = GetWindowInfo(pWindow);
  2556.         short            amount = 0;
  2557.         
  2558.         switch (part)
  2559.             {
  2560.             case kControlUpButtonPart:
  2561.                 amount = pData->vScrollAmount;
  2562.                 break;
  2563.                 
  2564.             case kControlDownButtonPart:
  2565.                 amount = -pData->vScrollAmount;
  2566.                 break;
  2567.                 
  2568.             // vertical page scrolling should be a multiple of the incremental scrolling -- so that
  2569.             // we avoid half-lines of text at the bottom of pages.
  2570.             
  2571.             // More generically, if there was a method for dealing with text scrolling by a non-constant
  2572.             // amount, this would be better -- but SimpleText currently doesn't have a framework to allow
  2573.             // the document object to override the scroll amount dynamically.  Maybe something to add in
  2574.             // the future.
  2575.             case kControlPageUpPart:
  2576.                 amount = (((pData->contentRect.bottom - pData->contentRect.top) / pData->vScrollAmount)-1) * pData->vScrollAmount;
  2577.                 if (amount == 0)
  2578.                     amount = pData->contentRect.bottom - pData->contentRect.top;
  2579.                 break;
  2580.  
  2581.             case kControlPageDownPart:
  2582.                 amount = (((pData->contentRect.top - pData->contentRect.bottom) / pData->vScrollAmount)+1) * pData->vScrollAmount;
  2583.                 if (amount == 0)
  2584.                     amount = pData->contentRect.top - pData->contentRect.bottom;
  2585.                 break;
  2586.             }
  2587.         
  2588.         SetControlAndClipAmount(control, &amount);
  2589.         if (amount != 0)
  2590.             DoScrollContent(pWindow, pData, 0, amount);
  2591.         }
  2592.         
  2593. } // VActionProc
  2594.  
  2595. #if GENERATINGCFM
  2596.     static RoutineDescriptor gVActionProcRD = BUILD_ROUTINE_DESCRIPTOR(uppControlActionProcInfo, VActionProc);
  2597.     static ControlActionUPP gVActionProc = &gVActionProcRD;
  2598. #else
  2599.     static ControlActionUPP gVActionProc = VActionProc;
  2600. #endif
  2601.  
  2602. // --------------------------------------------------------------------------------------------------------------
  2603. static pascal void HActionProc(ControlRef control, short part)
  2604. {
  2605.     if (part != 0)
  2606.         {
  2607.         WindowRef        pWindow = (**control).contrlOwner;
  2608.         WindowDataPtr     pData = GetWindowInfo(pWindow);
  2609.         short            amount = 0;
  2610.         
  2611.         switch (part)
  2612.             {
  2613.             case kControlUpButtonPart:
  2614.                 amount = pData->hScrollAmount;
  2615.                 break;
  2616.                 
  2617.             case kControlDownButtonPart:
  2618.                 amount = -pData->hScrollAmount;
  2619.                 break;
  2620.                 
  2621.             case kControlPageUpPart:
  2622.                 amount = pData->contentRect.right - pData->contentRect.left;
  2623.                 break;
  2624.  
  2625.             case kControlPageDownPart:
  2626.                 amount = pData->contentRect.left - pData->contentRect.right;
  2627.                 break;
  2628.             }
  2629.         
  2630.         SetControlAndClipAmount(control, &amount);
  2631.         if (amount != 0)
  2632.             DoScrollContent(pWindow, pData, amount, 0);
  2633.         }
  2634.         
  2635. } // HActionProc
  2636.  
  2637. #if GENERATINGCFM
  2638.     static RoutineDescriptor gHActionProcRD = BUILD_ROUTINE_DESCRIPTOR(uppControlActionProcInfo, HActionProc);
  2639.     static ControlActionUPP gHActionProc = &gHActionProcRD;
  2640. #else
  2641.     static ControlActionUPP gHActionProc = HActionProc;
  2642. #endif
  2643.  
  2644. // --------------------------------------------------------------------------------------------------------------
  2645. // END SCROLL ACTION PROCS
  2646. // --------------------------------------------------------------------------------------------------------------
  2647.  
  2648. #pragma segment Main
  2649.  
  2650. static OSErr DoContentClick(WindowRef pWindow)
  2651. {
  2652.     OSErr            anErr = noErr;
  2653.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  2654.     
  2655.     
  2656.     if ( pData )
  2657.         {
  2658.         SetPort((GrafPtr) GetWindowPort(pWindow));
  2659.         
  2660.         if (pData->pContentClick)
  2661.             {
  2662.             // let the object handle the click if it wishes to
  2663.             anErr = (*(pData->pContentClick)) (pWindow, pData, &gEvent);
  2664.             }
  2665.         
  2666.         if (anErr == noErr) 
  2667.             {
  2668.             ControlRef        theControl;
  2669.             short            part;
  2670.             
  2671.             GlobalToLocal(&gEvent.where);
  2672.             part = FindControl(gEvent.where, pWindow, &theControl);
  2673.             switch (part)
  2674.                 {
  2675.                 // do nothing for viewRect case
  2676.                 case 0:
  2677.                     break;
  2678.  
  2679.                 // track the thumb, and then update all at once
  2680.                 case kControlIndicatorPart:
  2681.                     {
  2682.                     short    value = GetControlValue(theControl);
  2683.                     
  2684.                     part = TrackControl(theControl, gEvent.where, nil);
  2685.                     if (part != 0)
  2686.                         {
  2687.                         // turn the value into a delta
  2688.                         value -= GetControlValue(theControl);
  2689.                         
  2690.                         // if we actually moved
  2691.                         if (value != 0)
  2692.                             {
  2693.                             if (theControl == pData->hScroll)
  2694.                                 DoScrollContent(pWindow, pData, value, 0);
  2695.                             if (theControl == pData->vScroll)
  2696.                                 DoScrollContent(pWindow, pData, 0, value);
  2697.                                 
  2698.                             }
  2699.                         }
  2700.                     }
  2701.                     break;
  2702.                     
  2703.                 // track the control, and scroll as we go
  2704.                 default:
  2705.                     if (theControl)
  2706.                         {
  2707.                         if (theControl == pData->hScroll)
  2708.                             part = TrackControl(theControl, gEvent.where, gHActionProc);
  2709.                         if (theControl == pData->vScroll)
  2710.                             part = TrackControl(theControl, gEvent.where, gVActionProc);
  2711.                         }
  2712.                     break;
  2713.                 }
  2714.             }
  2715.  
  2716.         }
  2717.  
  2718.         
  2719.     return anErr;
  2720.     
  2721. } // DoContentClick
  2722.  
  2723. // --------------------------------------------------------------------------------------------------------------
  2724. #pragma segment Main
  2725.  
  2726. static OSErr DoGrowWindow(WindowRef pWindow, EventRecord *pEvent)
  2727. {
  2728.     OSErr            anErr = noErr;
  2729.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  2730.     Rect            tempRect;
  2731.     LongRect        docRect;
  2732.     long            growResult;
  2733.     
  2734.     if (pData)
  2735.         {
  2736.         GrafPtr    pPort = (GrafPtr)GetWindowPort(pWindow);
  2737.         
  2738.         SetPort(pPort);
  2739.         
  2740.         RectToLongRect(&pData->contentRect, &docRect);
  2741.         if (pData->pGetDocumentRect)
  2742.             (*(pData->pGetDocumentRect)) (pWindow, pData, &docRect, true);
  2743.         if (pData->vScroll)
  2744.             docRect.right += 16;
  2745.         if (pData->hScroll)
  2746.             docRect.bottom += 16;
  2747.         
  2748.         if ( (pData->hasGrow) && (pData->hScroll == nil) && (pData->vScroll == nil) )
  2749.             {
  2750.             docRect.right += 16;
  2751.             docRect.bottom += 16;
  2752.             }
  2753.             
  2754.         // set up resize constraints
  2755.         tempRect.left = pData->minHSize;
  2756.         if (tempRect.left == 0)
  2757.             tempRect.left = kMinDocSize;
  2758.         tempRect.right = docRect.right - docRect.left;
  2759.         if (tempRect.right < tempRect.left)
  2760.             tempRect.right = tempRect.left;
  2761.         tempRect.top = pData->minVSize;
  2762.         if (tempRect.top == 0)
  2763.             tempRect.top = kMinDocSize;
  2764.         tempRect.bottom = docRect.bottom - docRect.top;
  2765.         if (tempRect.bottom < tempRect.top)
  2766.             tempRect.bottom = tempRect.top;
  2767.             
  2768.         growResult = GrowWindow(pWindow, pEvent->where, &tempRect);
  2769.         if ( growResult != 0 ) 
  2770.             {
  2771.             Rect        oldRect;
  2772.             RgnHandle    currentInval = NewRgn();
  2773.             Boolean        needInvalidate;
  2774.             
  2775.             // save old content area
  2776.             oldRect = pData->contentRect;
  2777.             
  2778.             // save old pending update
  2779.             GetWindowUpdateRgn(pWindow, currentInval);
  2780.             OffsetRgn(currentInval, pPort->portBits.bounds.left, pPort->portBits.bounds.top);
  2781.             
  2782.             // grow window and recalc what is needed
  2783.             SizeWindow(pWindow, growResult & 0xFFFF, growResult >> 16, true);
  2784.             AdjustScrollBars(pWindow, true, true, &needInvalidate);
  2785.             
  2786.             if (needInvalidate)
  2787.                 {
  2788.                 InvalRect(&pData->contentRect);
  2789.                 }
  2790.             else
  2791.                 {
  2792.                 // don't bother to redraw things that haven't changed
  2793.                 SectRect(&oldRect, &pData->contentRect, &oldRect);
  2794.                 ValidRect(&oldRect);
  2795.                 
  2796.                 // but, if a pending update was there, be sure to deal with that!
  2797.                 InvalRgn(currentInval);
  2798.                 }
  2799.  
  2800.             // if we have offset scrollbars, then force a redraw of them
  2801.             if (pData->hScrollOffset)
  2802.                 {
  2803.                 oldRect = GetWindowPort(pWindow)->portRect;
  2804.                 oldRect.right = oldRect.left + pData->hScrollOffset;
  2805.                 oldRect.top = oldRect.bottom - kScrollBarSize;
  2806.                 InvalRect(&oldRect);
  2807.                 }
  2808.             if (pData->vScrollOffset)
  2809.                 {
  2810.                 oldRect = GetWindowPort(pWindow)->portRect;
  2811.                 oldRect.bottom = oldRect.top + pData->vScrollOffset;
  2812.                 oldRect.left = oldRect.right - kScrollBarSize;
  2813.                 InvalRect(&oldRect);
  2814.                 }
  2815.  
  2816.             DisposeRgn(currentInval);
  2817.             }
  2818.             
  2819.         }
  2820.         
  2821.     
  2822.     return anErr;
  2823.     
  2824. } // DoGrowWindow
  2825.  
  2826. // --------------------------------------------------------------------------------------------------------------
  2827. #pragma segment Main
  2828.  
  2829. static OSErr DoZoomWindow(WindowRef pWindow, short zoomDir)
  2830. {
  2831.     Rect                *windRect, *zoomRect;
  2832.     Rect                globalPortRect, theSect, dGDRect;
  2833.     GDHandle            nthDevice, dominantGDevice;
  2834.     long                sectArea, greatestArea;
  2835.     short                 hMax, vMax;
  2836.     
  2837.     // determine the max size of the window
  2838.     {
  2839.     WindowDataPtr        pData = GetWindowInfo(pWindow);
  2840.     LongRect            docRect;
  2841.     
  2842.     RectToLongRect(&pData->contentRect, &docRect);
  2843.     if (pData->pGetDocumentRect)
  2844.         (*(pData->pGetDocumentRect)) (pWindow, pData, &docRect, true);
  2845.     if (pData->vScroll)
  2846.         docRect.right += kScrollBarSize;
  2847.     if (pData->hScroll)
  2848.         docRect.bottom += kScrollBarSize;
  2849.     
  2850.     if ( (pData->hasGrow) && (pData->hScroll == nil) && (pData->vScroll == nil) )
  2851.         {
  2852.         docRect.right += kScrollBarSize;
  2853.         docRect.bottom += kScrollBarSize;
  2854.         }
  2855.  
  2856.     hMax = docRect.right - docRect.left;
  2857.     vMax = docRect.bottom - docRect.top;
  2858.     }
  2859.     
  2860.     SetPort((GrafPtr) GetWindowPort(pWindow));
  2861.     EraseRect(&GetWindowPort(pWindow)->portRect);    // recommended for cosmetic reasons
  2862.  
  2863.     if (zoomDir == inZoomOut) 
  2864.         {
  2865.  
  2866.         /*
  2867.          *    ZoomWindow() is a good basic tool, but it doesn't do everything necessary to
  2868.          *    implement a good human interface when zooming. In fact it's not even close for
  2869.          *    more high-end hardware configurations. We must help it along by calculating an
  2870.          *    appropriate window size and location any time a window zooms out.
  2871.          */
  2872.         {
  2873.         RgnHandle    structRgn = NewRgn();
  2874.         
  2875.         GetWindowStructureRgn(pWindow, structRgn);
  2876.         windRect = &(**structRgn).rgnBBox;
  2877.         DisposeRgn(structRgn);
  2878.         }
  2879.         dominantGDevice = nil;
  2880.         if (gMachineInfo.theEnvirons.hasColorQD) 
  2881.             {
  2882.  
  2883.             /*
  2884.              *    Color QuickDraw implies the possibility of multiple monitors. This is where
  2885.              *    zooming becomes more interesting. One should zoom onto the monitor containing
  2886.              *    the greatest portion of the window. This requires walking the gDevice list.
  2887.              */
  2888.  
  2889.             nthDevice = GetDeviceList();
  2890.             greatestArea = 0;
  2891.             while (nthDevice != nil) 
  2892.                 {
  2893.                 if (TestDeviceAttribute(nthDevice, screenDevice)) 
  2894.                     {
  2895.                     if (TestDeviceAttribute(nthDevice, screenActive)) 
  2896.                         {
  2897.                         SectRect(windRect, &(**nthDevice).gdRect, &theSect);
  2898.                         sectArea = (long) RectWidth(theSect) * (long) RectHeight(theSect);
  2899.                         if (sectArea > greatestArea) 
  2900.                             {
  2901.                             greatestArea = sectArea;        // save the greatest intersection
  2902.                             dominantGDevice = nthDevice;    // and which device it belongs to
  2903.                             }
  2904.                         }
  2905.                     }
  2906.                 nthDevice = GetNextDevice(nthDevice);
  2907.                 }
  2908.             }
  2909.  
  2910.         /*
  2911.          *    At this point, we know the dimensions of the window we're zooming, and we know
  2912.          *    what screen we're going to put it on. To be more specific, however, we need a
  2913.          *    rectangle which defines the maximum dimensions of the resized window's contents.
  2914.          *    This rectangle accounts for the thickness of the window frame, the menu bar, and
  2915.          *    one or two pixels around the edges for cosmetic compatibility with ZoomWindow().
  2916.          */
  2917.  
  2918.         if (dominantGDevice != nil) 
  2919.             {
  2920.             dGDRect = (**dominantGDevice).gdRect;
  2921.             if (dominantGDevice == GetMainDevice())        // account for menu bar on main device
  2922.                 dGDRect.top += GetMBarHeight();
  2923.             }
  2924.         else 
  2925.             {
  2926.             dGDRect = qd.screenBits.bounds;                // if no gDevice, use default monitor
  2927.             dGDRect.top += GetMBarHeight();
  2928.             }
  2929.  
  2930.         globalPortRect = GetWindowPort(pWindow)->portRect;
  2931.         LocalToGlobal(&TopLeft(globalPortRect));        // calculate the window's portRect
  2932.         LocalToGlobal(&BotRight(globalPortRect));        // in global coordinates
  2933.  
  2934.         // account for the window frame and inset it a few pixels
  2935.         dGDRect.left    += 2 + globalPortRect.left - windRect->left;
  2936.         dGDRect.top        += 2 + globalPortRect.top - windRect->top;
  2937.         dGDRect.right    -= 1 + windRect->right - globalPortRect.right;
  2938.         dGDRect.bottom    -= 1 + windRect->bottom - globalPortRect.bottom;
  2939.  
  2940.         /*
  2941.          *    Now we know exactly what our limits are, and since there are input parameters
  2942.          *    specifying the dimensions we'd like to see, we can move and resize the zoom
  2943.          *    state rectangle for the best possible results. We have three goals in this:
  2944.          *    1. Display the window entirely visible on a single device.
  2945.          *    2. Resize the window to best represent the dimensions of the document itself.
  2946.          *    3. Move the window as short a distance as possible to achieve #1 and #2.
  2947.          */
  2948.  
  2949.         zoomRect = &(**(WStateDataHandle) ((WindowPeek) pWindow)->dataHandle).stdState;
  2950.  
  2951.         /*
  2952.          *    Initially set the zoom rectangle to the size requested by the input parameters,
  2953.          *    although not smaller than a minimum size. We do this without moving the origin.
  2954.          */
  2955.  
  2956.         zoomRect->right = (zoomRect->left = globalPortRect.left) +
  2957.                                 Max(hMax, kMinDocSize);
  2958.         zoomRect->bottom = (zoomRect->top = globalPortRect.top) +
  2959.                                 Max(vMax, kMinDocSize);
  2960.  
  2961.         // Shift the entire rectangle if necessary to bring its origin inside dGDRect.
  2962.         OffsetRect(zoomRect,
  2963.                     Max(dGDRect.left - zoomRect->left, 0),
  2964.                     Max(dGDRect.top - zoomRect->top, 0));
  2965.  
  2966.         /*
  2967.          *    Shift the rectangle up and/or to the left if necessary to accomodate the view,
  2968.          *    and if it is possible to do so. The rectangle may not be moved such that its
  2969.          *    origin would fall outside of dGDRect.
  2970.          */
  2971.  
  2972.         OffsetRect(zoomRect,
  2973.                     -Pin(zoomRect->right - dGDRect.right, 0, zoomRect->left - dGDRect.left),
  2974.                     -Pin(zoomRect->bottom - dGDRect.bottom, 0, zoomRect->top - dGDRect.top));
  2975.  
  2976.         // Clip expansion to dGDRect, in case view is larger than dGDRect.
  2977.         zoomRect->right = Min(zoomRect->right, dGDRect.right);
  2978.         zoomRect->bottom = Min(zoomRect->bottom, dGDRect.bottom);
  2979.         }
  2980.  
  2981.     ZoomWindow(pWindow, zoomDir, pWindow == FrontWindow());
  2982.     
  2983.     AdjustScrollBars(pWindow, true, true, nil);
  2984.  
  2985.     InvalRect(&GetWindowPort(pWindow)->portRect);
  2986.     
  2987.     return noErr;
  2988.     
  2989. } // DoZoomWindow
  2990.  
  2991. // --------------------------------------------------------------------------------------------------------------
  2992. #pragma segment Main
  2993.  
  2994. OSErr DoActivate(WindowRef pWindow, Boolean activating)
  2995. {
  2996.  
  2997.     OSErr            anErr = noErr;
  2998.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  2999.     
  3000.     SetPort((GrafPtr) GetWindowPort(pWindow));
  3001.     
  3002.     if ( pData )
  3003.         {
  3004.         if (pData->pActivateEvent)
  3005.             anErr = (*(pData->pActivateEvent)) (pWindow, pData, activating);
  3006.             
  3007.         if (anErr == noErr)
  3008.             {
  3009.             if (activating)
  3010.                 {
  3011.                 // Reshow all controls on activation
  3012.                 if (pData->hScroll)
  3013.                     ShowControl(pData->hScroll);
  3014.                 if (pData->vScroll)
  3015.                     ShowControl(pData->vScroll);
  3016.                 }
  3017.             else
  3018.                 {
  3019.                 // Hide all controls on deactivation
  3020.                 if (pData->hScroll)
  3021.                     HideControl(pData->hScroll);
  3022.                 if (pData->vScroll)
  3023.                     HideControl(pData->vScroll);
  3024.                 }
  3025.                 
  3026.             if (pData->hasGrow) 
  3027.                 {
  3028.                 Rect    growIconRect;
  3029.                 
  3030.                 CalculateGrowIcon(pData, &growIconRect);
  3031.                 InvalRect(&growIconRect);
  3032.                 }
  3033.             }
  3034.         }
  3035.  
  3036.     AdjustMenus(pWindow, true, false);
  3037.         
  3038.     return anErr;
  3039.     
  3040. } // DoActivate
  3041.  
  3042. // --------------------------------------------------------------------------------------------------------------
  3043. #pragma segment Main
  3044.  
  3045. static OSErr    DoStartupGX(void)
  3046. {
  3047.     gxGraphicsError    anErr = noErr;
  3048.     
  3049.     if (!gMachineInfo.haveStartedGX)
  3050.         {
  3051.         GXEnterGraphics();
  3052.         GXGetGraphicsError(&anErr);
  3053.         if ( (anErr == noErr) && (GXGetGraphicsClient() == nil) )
  3054.             anErr = out_of_memory;
  3055.         if (anErr == noErr)
  3056.             {
  3057.             anErr = GXInitPrinting();
  3058.             if (anErr != noErr)
  3059.                 GXExitGraphics();
  3060.             }
  3061.             
  3062.         if (anErr != noErr)
  3063.             GXSetGraphicsClient(nil);
  3064.         }
  3065.         
  3066.     if (GXGetGraphicsClient() == nil)
  3067.         anErr = out_of_memory;
  3068.  
  3069.     if (anErr == noErr)
  3070.         gMachineInfo.haveStartedGX = true;
  3071.  
  3072.     return anErr;
  3073.     
  3074. } // DoStartupGX
  3075.  
  3076. // --------------------------------------------------------------------------------------------------------------
  3077. #pragma segment Main
  3078.  
  3079. OSErr    DoDefault(WindowDataPtr     pData)
  3080. {
  3081.     OSErr    anErr = noErr;
  3082.     
  3083.     if (gMachineInfo.haveGX)
  3084.         {
  3085.         // start up GX, if needed
  3086.         anErr = DoStartupGX();
  3087.  
  3088.         if (anErr == noErr)
  3089.             {
  3090.             
  3091.             // default the job if we don't have it
  3092.             if (pData->hPrint == nil)
  3093.                 {
  3094.                 anErr = GXNewJob((gxJob*)&pData->hPrint);
  3095.                 if (anErr == noErr)
  3096.                     {
  3097.                     GXInstallApplicationOverride(pData->hPrint, gxPrintingEventMsg,         gPrintingEventMessage);
  3098.                     GXInstallApplicationOverride(pData->hPrint, gxCompleteSpoolFileMsg,     gCompleteSpoolFileMessage);
  3099.                     }
  3100.                 }
  3101.             }
  3102.         }
  3103.     else
  3104.         {
  3105.         PrOpen();
  3106.         anErr = PrError();
  3107.         if (anErr == noErr)
  3108.             {
  3109.             if (pData->hPrint == nil)
  3110.                 {
  3111.                 pData->hPrint = NewHandleClear(sizeof(TPrint));
  3112.                 anErr = MemError();
  3113.                 if (anErr == noErr)
  3114.                     PrintDefault(pData->hPrint);
  3115.                 }
  3116.                 
  3117.             }
  3118.         PrClose();
  3119.         }
  3120.         
  3121.     return anErr;
  3122.     
  3123. } // DoDefault
  3124.  
  3125. // --------------------------------------------------------------------------------------------------------------
  3126. #pragma segment Main
  3127.  
  3128. static void SetupForPrintDialogs(gxEditMenuRecord * pEdit)
  3129. {
  3130.     MenuHandle    menu;
  3131.     short        menuID, itemID;
  3132.  
  3133.     CommandToIDs(cCut,   &pEdit->editMenuID, &pEdit->cutItem);
  3134.     CommandToIDs(cCopy,  &pEdit->editMenuID, &pEdit->copyItem);
  3135.     CommandToIDs(cPaste, &pEdit->editMenuID, &pEdit->pasteItem);
  3136.     CommandToIDs(cClear, &pEdit->editMenuID, &pEdit->clearItem);
  3137.     CommandToIDs(cUndo,  &pEdit->editMenuID, &pEdit->undoItem);
  3138.     
  3139.     // diable everything we don't want to deal with
  3140.     for (menuID = mApple; menuID <= mLastMenu; ++menuID)
  3141.         {
  3142.         menu = GetMenuHandle(menuID);
  3143.         
  3144.         if (menu)
  3145.             {
  3146.             switch (menuID)
  3147.                 {
  3148.                 case mApple:
  3149.                     CommandToIDs(cAbout, &menuID, &itemID);
  3150.                     DisableItem(menu, itemID);
  3151.                     break;
  3152.                     
  3153.                 case mEdit:
  3154.                     CommandToIDs(cSelectAll, &menuID, &itemID);
  3155.                     DisableItem(menu, itemID);
  3156.                     CommandToIDs(cShowClipboard, &menuID, &itemID);
  3157.                     DisableItem(menu, itemID);
  3158.                     break;
  3159.                     
  3160.                 default:
  3161.                     DisableItem(menu, 0);
  3162.                     break;
  3163.                 }
  3164.             }
  3165.         }
  3166.     
  3167.     // Disable the current indicator because the dialogs are moveable modal    
  3168.     HiliteMenu(0);
  3169.         
  3170. } // SetupForPrintDialogs
  3171.  
  3172. // --------------------------------------------------------------------------------------------------------------
  3173. #pragma segment Main
  3174.  
  3175. OSErr    DoPageSetup(WindowRef pWindow)
  3176. {
  3177.     OSErr            anErr = noErr;
  3178.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  3179.         
  3180.     anErr = DoDefault(pData);
  3181.     nrequire(anErr, DoDefault);
  3182.     
  3183.     if (gMachineInfo.haveGX)
  3184.         {
  3185.         gxEditMenuRecord    theEdit;
  3186.         
  3187.         SetupForPrintDialogs(&theEdit);
  3188.         
  3189.         GXJobDefaultFormatDialog(pData->hPrint, &theEdit);
  3190.         anErr = GXGetJobError(pData->hPrint);
  3191.             
  3192.         AdjustMenus(pWindow, true, true);
  3193.         }
  3194.     else
  3195.         {
  3196.         PrOpen();
  3197.         anErr = PrError();
  3198.         if (anErr == noErr)
  3199.             {
  3200.             SetCursor(&qd.arrow);
  3201.             PrStlDialog(pData->hPrint);
  3202.             }
  3203.         PrClose();
  3204.         }
  3205.  
  3206. // FALL THROUGH EXCEPTION HANDLING
  3207. DoDefault:        
  3208.     return anErr;
  3209.     
  3210. } // DoPageSetup
  3211.  
  3212. // --------------------------------------------------------------------------------------------------------------
  3213. #pragma segment Main
  3214.  
  3215. static OSErr    DoPrintSetup(WindowRef pWindow, StringPtr pPrinterName)
  3216. {
  3217.     OSErr            anErr = noErr;
  3218.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  3219.         
  3220.     anErr = DoDefault(pData);
  3221.     nrequire(anErr, DoDefault);
  3222.     
  3223.     if (gMachineInfo.haveGX)
  3224.         {
  3225.         gxEditMenuRecord    theEdit;
  3226.         gxDialogResult        result;
  3227.         
  3228.         // toss any previous errors that might be around
  3229.         (void)GXGetJobError(pData->hPrint);
  3230.         
  3231.         if ( (pPrinterName) && (pPrinterName[0] != 0) )
  3232.             GXSelectJobOutputPrinter(pData->hPrint, pPrinterName);
  3233.         else
  3234.             {
  3235.             SetupForPrintDialogs(&theEdit);
  3236.             result = GXJobPrintDialog(pData->hPrint, &theEdit);
  3237.             AdjustMenus(pWindow, true, true);
  3238.             }
  3239.         if (anErr == noErr)
  3240.             {
  3241.             anErr = GXGetJobError(pData->hPrint);
  3242.             if ( (anErr == noErr) && (result == gxCancelSelected) )
  3243.                 anErr = eUserCanceled;
  3244.             }
  3245.         }
  3246.     else
  3247.         {
  3248.         PrOpen();
  3249.         anErr = PrError();
  3250.         if (anErr == noErr)
  3251.             {
  3252.             SetCursor(&qd.arrow);
  3253.             if (PrJobDialog(pData->hPrint) == false)
  3254.                 anErr = eUserCanceled;
  3255.             }
  3256.             
  3257.         PrClose();
  3258.         }
  3259.         
  3260. // FALL THROUGH EXCEPTION HANDLING
  3261. DoDefault:        
  3262.     return anErr;
  3263.     
  3264. } // DoPrintSetup
  3265.  
  3266. // --------------------------------------------------------------------------------------------------------------
  3267. #pragma segment Main
  3268.  
  3269. static OSErr    DoPrint(WindowRef pWindow, void * hPrint, Boolean oneCopy)
  3270. {
  3271.     gxGraphicsError        anErr = noErr;
  3272.     WindowDataPtr         pData = GetWindowInfo(pWindow);
  3273.     Boolean                didAllocate = false;
  3274.     
  3275.     // use a watch cursor while printing
  3276.     SetWatchCursor();
  3277.     
  3278.     if (gMachineInfo.haveGX)
  3279.         {
  3280.         // startup GX, if needed
  3281.         anErr = DoStartupGX();
  3282.  
  3283.         if ( (anErr == noErr) && (hPrint == nil) )
  3284.             {
  3285.             anErr = GXNewJob((gxJob*)&hPrint);
  3286.             if (anErr == noErr)            
  3287.                 {
  3288.                 GXInstallApplicationOverride(hPrint, gxPrintingEventMsg,         gPrintingEventMessage);
  3289.                 GXInstallApplicationOverride(hPrint, gxCompleteSpoolFileMsg,     gCompleteSpoolFileMessage);
  3290.                 didAllocate = true;
  3291.                 }
  3292.             }
  3293.  
  3294.         if (anErr == noErr)
  3295.             {            
  3296.             Str255    docName;
  3297.             
  3298.             if (oneCopy)
  3299.                 {
  3300.                 gxCopiesInfo theCopies;
  3301.                 
  3302.                 theCopies.copies = 1;
  3303.                 AddCollectionItem(GXGetJobCollection(hPrint), gxCopiesTag, gxPrintingTagID, sizeof(theCopies), &theCopies);
  3304.                 }
  3305.                 
  3306.             GXSetJobRefCon(hPrint, pData);
  3307.             
  3308.             GetWTitle(pWindow, docName);
  3309.             GXStartJob(hPrint, docName, 0);
  3310.             anErr = GXGetJobError(hPrint);
  3311.             if (anErr == noErr)
  3312.                 {
  3313.                 long        first, last, pageIndex;
  3314.                 Rect        pageRect;
  3315.                 CGrafPort    thePort;
  3316.                 
  3317.                 // determine size of page, and number of pages
  3318.                 {
  3319.                 gxRectangle    pageSize;
  3320.                 GXGetFormatDimensions(GXGetJobFormat(hPrint, 1), &pageSize, nil);
  3321.                 pageRect.top    = pageSize.top >> 16;
  3322.                 pageRect.left    = pageSize.left >> 16;
  3323.                 pageRect.bottom    = pageSize.bottom >> 16;
  3324.                 pageRect.right    = pageSize.right >> 16;
  3325.                 }            
  3326.                 GXGetJobPageRange(hPrint, &first, &last);
  3327.                 anErr = GXGetJobError(hPrint);
  3328.                 if (first < 1)
  3329.                     first = 1;
  3330.                 if (last < first)
  3331.                     last = first;
  3332.                     
  3333.                 if (anErr == noErr)
  3334.                     {
  3335.                     // make a port to perform translation in
  3336.                     OpenCPort(&thePort);
  3337.                     
  3338.                     for (pageIndex = first; pageIndex <= last; ++pageIndex)
  3339.                         {
  3340.                         SetPort((GrafPtr) &thePort);
  3341.                         
  3342.                         if (pData->documentOutputsGX)
  3343.                             anErr = (*(pData->pPrintPage)) (pWindow, pData, &pageRect, &pageIndex);
  3344.                         else
  3345.                             {
  3346.                             Point                    patStretch = {1,1};
  3347.                             gxTranslationOption     options = gxOptimizedTranslation;
  3348.                             CatchRefCon                theRefCon;
  3349.                             
  3350.                             if (GXGetPrinterDriverType(GXGetJobPrinter(hPrint)) == 'post')
  3351.                                 options += gxPostScriptTargetTranslation;
  3352.                             else
  3353.                                 options += gxRasterTargetTranslation;
  3354.                             
  3355.                             theRefCon.theJob                    = hPrint;
  3356.                             theRefCon.doLayout                    = (pData->originalFileType == 'TEXT');
  3357.                             theRefCon.thePage                     = GXNewShape(gxPictureType);
  3358.                             theRefCon.thePageRectangle.top         = ff(pageRect.top);
  3359.                             theRefCon.thePageRectangle.left     = ff(pageRect.left);
  3360.                             theRefCon.thePageRectangle.bottom     = ff(pageRect.bottom);
  3361.                             theRefCon.thePageRectangle.right     = ff(pageRect.right);
  3362.     
  3363.                             GXInstallQDTranslator(
  3364.                                         qd.thePort,
  3365.                                         options,
  3366.                                         &pageRect,
  3367.                                         &pageRect,
  3368.                                         patStretch,
  3369.                                         gSimpleCatchShape,
  3370.                                         &theRefCon);
  3371.                             GXGetGraphicsError(&anErr);
  3372.                             
  3373.                             if (anErr == noErr)
  3374.                                 {
  3375.                                 long    whichPage = pageIndex;
  3376.                                 
  3377.                                 anErr = (*(pData->pPrintPage)) (pWindow, pData, &pageRect, &whichPage);
  3378.                                 GXRemoveQDTranslator(qd.thePort, nil);
  3379.                                 
  3380.                                 GXPrintPage(hPrint, pageIndex, nil, theRefCon.thePage);
  3381.                                 anErr = GXGetJobError(hPrint);
  3382.                                 pageIndex = whichPage;
  3383.                                 }
  3384.                             
  3385.                             GXDisposeShape(theRefCon.thePage);
  3386.                             }
  3387.                         
  3388.                         if (anErr == noErr)
  3389.                             GXGetGraphicsError(&anErr);
  3390.                         
  3391.                         // bail when we are told to stop
  3392.                         if ( (pageIndex == -1) || (anErr != noErr) )
  3393.                             break;
  3394.                         }
  3395.                     
  3396.                     // all done with our temp port and job
  3397.                     CloseCPort(&thePort);
  3398.                     }
  3399.                     
  3400.                 GXFinishJob(hPrint);
  3401.                 if (anErr == noErr) anErr = GXGetJobError(hPrint);
  3402.                 }
  3403.                 
  3404.             }
  3405.                 
  3406.         if (didAllocate)
  3407.             GXDisposeJob(hPrint);
  3408.  
  3409.         // restore those menus!
  3410.         AdjustMenus(pWindow, true, true);
  3411.  
  3412.         }
  3413.     else
  3414.         {
  3415.         TPPrPort    printingPort;
  3416.         
  3417.         PrOpen();
  3418.         anErr = PrError();
  3419.         if (anErr == noErr)
  3420.             {
  3421.             if (hPrint == nil)
  3422.                 {
  3423.                 hPrint = NewHandleClear(sizeof(TPrint));
  3424.                 anErr = MemError();
  3425.                 if (anErr == noErr)
  3426.                     {
  3427.                     PrintDefault(hPrint);
  3428.                     didAllocate = true;
  3429.                     }
  3430.                 }
  3431.             
  3432.             if (anErr == noErr)
  3433.                 {
  3434.                 short    firstPage, lastPage;
  3435.                 
  3436.                 // be sure to get the page range BEFORE calling PrValidate(), 
  3437.                 // which blows it away for many drivers.
  3438.                 firstPage = (**(THPrint)hPrint).prJob.iFstPage;
  3439.                 lastPage = (**(THPrint)hPrint).prJob.iLstPage;
  3440.                 
  3441.                 // make sure the print record is cool to use
  3442.                 PrValidate(hPrint);
  3443.  
  3444.                 // then clear out the job itself -- some drivers don't
  3445.                 // do this from within PrValidate().  We want the job
  3446.                 // clear in case the driver bases background behavior
  3447.                 // from this range (and many do).
  3448.                 (**(THPrint)hPrint).prJob.iFstPage = 1;
  3449.                 (**(THPrint)hPrint).prJob.iLstPage = 9999;
  3450.                 
  3451.                 if (oneCopy)
  3452.                     (** ((THPrint)hPrint)).prJob.iCopies = 1;
  3453.                     
  3454.                 // start printing, and then loop over the pages
  3455.                 printingPort = PrOpenDoc(hPrint, nil, nil);
  3456.                 anErr = PrError();
  3457.                 if (anErr == noErr)
  3458.                     {
  3459.                     long    pageIndex;
  3460.                     Rect    pageRect;
  3461.                     
  3462.                     SetPort((GrafPtr) printingPort);
  3463.                     
  3464.                     pageRect = (**(THPrint)hPrint).prInfo.rPage;
  3465.                     if (firstPage < 1)
  3466.                         firstPage = 1;
  3467.                     if (lastPage < firstPage)
  3468.                         lastPage = firstPage;
  3469.                     for (pageIndex = firstPage; pageIndex <= lastPage; ++pageIndex)
  3470.                         {
  3471.                         PrOpenPage(printingPort, nil);
  3472.                         anErr = PrError();
  3473.                         if (anErr == noErr)
  3474.                             anErr = (*(pData->pPrintPage)) (pWindow, pData, &pageRect, &pageIndex);
  3475.                             
  3476.                         PrClosePage(printingPort);
  3477.                         if (anErr == noErr)
  3478.                             anErr = PrError();
  3479.                         if ( (anErr != noErr) || (pageIndex == -1) )
  3480.                             break;
  3481.                         }
  3482.                     }
  3483.                     
  3484.                 // Finish up printing of the document
  3485.                 PrCloseDoc(printingPort);
  3486.                 if (anErr == noErr)
  3487.                     anErr = PrError();
  3488.                 if ( (anErr == noErr) && ((**(THPrint)hPrint).prJob.bJDocLoop == bSpoolLoop) )
  3489.                     {
  3490.                     TPrStatus    theStatus;
  3491.                     
  3492.                     PrPicFile(hPrint, nil, nil, nil, &theStatus);
  3493.                     anErr = PrError();
  3494.                     }
  3495.                 }
  3496.                 
  3497.             if (didAllocate)
  3498.                 DisposeHandle((Handle) hPrint);
  3499.                 
  3500.             }
  3501.         PrClose();
  3502.         }
  3503.     
  3504.         
  3505.     // restore cursor
  3506.     SetCursor(&qd.arrow);
  3507.  
  3508.     return anErr;
  3509.     
  3510. } // DoPrint
  3511.  
  3512. // --------------------------------------------------------------------------------------------------------------
  3513. #pragma segment Main
  3514.  
  3515. OSErr    DoCommand(WindowRef pWindow, short commandID, long menuResult)
  3516. {
  3517.     OSErr            anErr = noErr;
  3518.     WindowDataPtr     pData = nil;
  3519.     
  3520.     if (pWindow)
  3521.         {
  3522.         pData = (WindowDataPtr) GetWindowInfo(pWindow);
  3523.         
  3524.         if ( (pData) && (pData->pCommand) )
  3525.             anErr = (*(pData->pCommand)) (pWindow, pData, commandID, menuResult);
  3526.         }
  3527.     
  3528.     if (anErr == noErr)
  3529.         {
  3530.         // default command handling
  3531.         switch (commandID)
  3532.             {
  3533.             // About box command
  3534.             case cAbout:
  3535.                 if (!BringToFrontIfExists(kAboutWindow))
  3536.                     anErr = MakeNewWindow(kAboutWindow, nil, '????', nil);
  3537.                 break;
  3538.                 
  3539.             case cDeskAccessory:
  3540.                 {
  3541.                 Str255    tempString;
  3542.                 
  3543.                 GetMenuItemText(GetMenuHandle(menuResult>>16), menuResult & 0xFFFF, tempString);
  3544.                 OpenDeskAcc(tempString);
  3545.                 }
  3546.                 break;
  3547.                 
  3548.             // New window command
  3549.             case cNew:
  3550.                 anErr = MakeNewWindow(kTextWindow, nil, 'TEXT', nil);
  3551.                 break;
  3552.                 
  3553.             // Open window command
  3554.             case cOpen:
  3555.                 anErr = DoOpenWindow();
  3556.                 break;
  3557.                 
  3558.             // Close window command
  3559.             case cClose:
  3560.                 anErr = DoCloseWindow(pWindow, false);
  3561.                 break;
  3562.                 
  3563.             case cPageSetup:
  3564.                 anErr = DoPageSetup(pWindow);
  3565.                 break;
  3566.                 
  3567.             case cPrint:
  3568.                 anErr = DoPrintSetup(pWindow, nil);
  3569.                 if (anErr == noErr)
  3570.                     anErr = DoPrint(pWindow, pData->hPrint, false);
  3571.                 break;
  3572.                 
  3573.             case cPrintOneCopy:
  3574.                 anErr = DoPrint(pWindow, pData->hPrint, true);
  3575.                 break;
  3576.                 
  3577.             // get out of here command!
  3578.             case cQuit:
  3579.                 gAllDone = true;
  3580.                 break;
  3581.     
  3582.             // show/hide clipboard
  3583.             case cShowClipboard:
  3584.                 if (!BringToFrontIfExists(kClipboardWindow))
  3585.                     {
  3586.                     anErr = MakeNewWindow(kClipboardWindow, nil, '????', nil);
  3587.                     }
  3588.                 else
  3589.                     {
  3590.                     pWindow = FrontWindow();
  3591.                     anErr = DoCloseWindow(pWindow, false);
  3592.                     }
  3593.                 break;
  3594.                 
  3595.             case cNextPage:
  3596.                 gEvent.what = keyDown;
  3597.                 gEvent.message = kPageDown << 8;
  3598.                 gEvent.modifiers = 0;
  3599.                 DoKeyEvent(pWindow, &gEvent, false);
  3600.                 break;
  3601.                 
  3602.             case cPreviousPage:
  3603.                 gEvent.what = keyDown;
  3604.                 gEvent.message = kPageUp << 8;
  3605.                 gEvent.modifiers = 0;
  3606.                 DoKeyEvent(pWindow, &gEvent, false);
  3607.                 break;
  3608.                 
  3609.             // Do nothing command
  3610.             case cNull:
  3611.                 break;
  3612.                             
  3613.             default:
  3614.                 break;
  3615.             }
  3616.         }
  3617.         
  3618.     // don't report cancels
  3619.     if ( (anErr == iPrAbort) || (anErr == gxPrUserAbortErr) )
  3620.         anErr = noErr;
  3621.     
  3622.     // some errors map to other errors because they are basically the same
  3623.     // This way we can use the same string.
  3624.     if (anErr == out_of_memory)
  3625.         anErr = memFullErr;
  3626.         
  3627.     if ( (anErr != noErr) && (anErr != eActionAlreadyHandled) && (anErr != eUserCanceled) )
  3628.         {
  3629.         // some commands are so similar to other commands that we map their IDs
  3630.         // for the purposes of the error strings
  3631.         if (commandID == cSaveAs)
  3632.             commandID = cSave;
  3633.         if (commandID == cPrintOneCopy)
  3634.             commandID = cPrint;
  3635.             
  3636.         ConductErrorDialog(anErr, commandID, cancel);
  3637.         }
  3638.         
  3639.     // in any case, unhilite the menu selected after command processing is done    
  3640.     HiliteMenu(0);
  3641.     
  3642.     return anErr;
  3643.     
  3644. } // DoCommand
  3645.  
  3646. // --------------------------------------------------------------------------------------------------------------
  3647. #pragma segment Main
  3648.  
  3649. static OSErr    DoMenuCommand(WindowRef pWindow, long menuResult)
  3650. {
  3651.     OSErr    anErr = noErr;
  3652.     short    commandID = cNull;
  3653.     short    ** commandHandle;
  3654.     short    menuID = menuResult >> 16;
  3655.     
  3656.     if (menuID == kHMHelpMenuID)
  3657.         {
  3658.         // close existing database (if any)
  3659.         if ((gAGRefNum != -1) && AGIsDatabaseOpen(gAGRefNum))
  3660.             {
  3661.             AGClose(&gAGRefNum);
  3662.             gAGRefNum = -1;
  3663.             }
  3664.             
  3665.         // and open the database we have found in the past
  3666.         AGOpen(&gAGSpec, 0, nil, &gAGRefNum);
  3667.         }
  3668.     else
  3669.         {
  3670.         if (menuID >= mFontSubMenusStart)
  3671.             {
  3672.             commandID = cSelectFontStyle;
  3673.             }
  3674.         else
  3675.             {
  3676.             // read in the resource that controls this menu
  3677.                 {
  3678.                 short    oldResFile = CurResFile();
  3679.                 
  3680.                 UseResFile(gApplicationResFile);
  3681.                 commandHandle = (short**) Get1Resource('MCMD', menuID);
  3682.                 UseResFile(oldResFile);
  3683.                 anErr = ResError();
  3684.                 nrequire(anErr, FailedToLoadCommandTable);
  3685.                 }
  3686.             
  3687.             if (commandHandle)
  3688.                 {
  3689.                 short    item = menuResult & 0xFFFF;
  3690.                 short    * pCommands = *commandHandle;
  3691.                 
  3692.                 if (item <= pCommands[0])
  3693.                     commandID = pCommands[item];
  3694.                 else
  3695.                     commandID = pCommands[pCommands[0]];
  3696.                 }
  3697.             }
  3698.         
  3699.         anErr = DoCommand(pWindow, commandID, menuResult);
  3700.         }
  3701.     
  3702. // FALL THROUGH EXCEPTION HANDLING
  3703. FailedToLoadCommandTable:
  3704.  
  3705.     return anErr;
  3706.     
  3707. } // DoMenuCommand
  3708.  
  3709.  
  3710. // --------------------------------------------------------------------------------------------------------------
  3711. #pragma segment Main
  3712.  
  3713. static void DoKeyPageDown(WindowRef pWindow, WindowDataPtr pData, Boolean processPageControls)
  3714. {
  3715.  
  3716.     if (GetControlValue(pData->vScroll) < GetControlMaximum(pData->vScroll))
  3717.         VActionProc(pData->vScroll, kControlPageDownPart);
  3718.     else
  3719.         {
  3720.         if ( (processPageControls) && (IsCommandEnabled(cNextPage)) )
  3721.             {
  3722.             short amount;
  3723.  
  3724.             if (DoCommand(pWindow, cNextPage, 0) == eActionAlreadyHandled)
  3725.                 {
  3726.                 amount = GetControlValue(pData->vScroll);
  3727.                 SetControlAndClipAmount(pData->vScroll, &amount);
  3728.                 if (amount != 0)
  3729.                     DoScrollContent(pWindow, pData, 0, amount);
  3730.                 }
  3731.             
  3732.             AdjustMenus(pWindow, true, false);
  3733.             }
  3734.         }
  3735.     
  3736. } // DoKeyPageDown
  3737.  
  3738. // --------------------------------------------------------------------------------------------------------------
  3739. #pragma segment Main
  3740.  
  3741. static void DoKeyPageUp(WindowRef pWindow, WindowDataPtr pData, Boolean processPageControls)
  3742. {
  3743.     if (GetControlValue(pData->vScroll) > GetControlMinimum(pData->vScroll))
  3744.         VActionProc(pData->vScroll, kControlPageUpPart);
  3745.     else
  3746.         {
  3747.         if ( (processPageControls) && (IsCommandEnabled(cPreviousPage)) )
  3748.             {
  3749.             short amount;
  3750.             
  3751.             if (DoCommand(pWindow, cPreviousPage, 0) == eActionAlreadyHandled)
  3752.                 {
  3753.                 amount = -(GetControlMaximum(pData->vScroll)-GetControlValue(pData->vScroll));
  3754.                 SetControlAndClipAmount(pData->vScroll, &amount);
  3755.                 if (amount != 0)
  3756.                     DoScrollContent(pWindow, pData, 0, amount);
  3757.                 }
  3758.             
  3759.             AdjustMenus(pWindow, true, false);
  3760.             }
  3761.         }
  3762.         
  3763. } // DoKeyPageUp
  3764.  
  3765. // --------------------------------------------------------------------------------------------------------------
  3766. #pragma segment Main
  3767.  
  3768. OSErr    DoKeyEvent(WindowRef pWindow, EventRecord * pEvent, Boolean processPageControls)
  3769. {
  3770.     OSErr            anErr = noErr;
  3771.     WindowDataPtr     pData = nil;
  3772.     Boolean            passToObject = false;
  3773.     Boolean         isMotionKey = false;
  3774.     long            menuResult = 0;
  3775.     
  3776.     char keyCode = (pEvent->message >> 8) & charCodeMask;
  3777.  
  3778.     if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3779.         {
  3780.         AdjustMenus(pWindow, true, false);
  3781.         menuResult = MenuKey(pEvent->message & charCodeMask);
  3782.         DoMenuCommand(pWindow, menuResult);
  3783.         pWindow = FrontWindow();
  3784.         }
  3785.  
  3786.     if (menuResult == 0)
  3787.         {
  3788.         if (pWindow)
  3789.             {
  3790.             pData = (WindowDataPtr)GetWindowInfo(pWindow);
  3791.             if ( (pData) && (pData->pKeyEvent) )
  3792.                 passToObject = true;
  3793.             SetPort((GrafPtr) GetWindowPort(pWindow));
  3794.             }
  3795.             
  3796.         if (pData)
  3797.             {
  3798.             switch (keyCode)
  3799.                 {
  3800.                 case kHome: // top of file
  3801.                     isMotionKey = true;
  3802.                     if (pData->vScroll)
  3803.                         {
  3804.                         short amount;
  3805.                         
  3806.                         if ( (processPageControls) && (IsCommandEnabled(cGotoPage)) )
  3807.                             DoCommand(pWindow, cGotoPage, cGotoFirst);
  3808.  
  3809.                         amount = GetControlValue(pData->vScroll);
  3810.                         SetControlAndClipAmount(pData->vScroll, &amount);
  3811.                         if (amount != 0)
  3812.                             DoScrollContent(pWindow, pData, 0, amount);
  3813.                         passToObject = false;
  3814.                         }
  3815.                     break;
  3816.                     
  3817.                 case kEnd: // end of file
  3818.                     isMotionKey = true;
  3819.                     if (pData->vScroll)
  3820.                         {
  3821.                         short amount;
  3822.  
  3823.                         if ( (processPageControls) && (IsCommandEnabled(cGotoPage)) )
  3824.                             DoCommand(pWindow, cGotoPage, cGotoLast);
  3825.                             
  3826.                         amount = -(GetControlMaximum(pData->vScroll)-GetControlValue(pData->vScroll));
  3827.                         SetControlAndClipAmount(pData->vScroll, &amount);
  3828.                         if (amount != 0)
  3829.                             DoScrollContent(pWindow, pData, 0, amount);
  3830.                         passToObject = false;
  3831.                         }
  3832.                     break;
  3833.                     
  3834.                 case kPageUp: // scroll bar page up
  3835.                     isMotionKey = true;
  3836.                     if (pData->vScroll)
  3837.                         {
  3838.                         DoKeyPageUp(pWindow, pData, processPageControls);
  3839.                         passToObject = false;
  3840.                         }
  3841.                     break;
  3842.                     
  3843.                 case kPageDown: // scroll bar page down
  3844.                     isMotionKey = true;
  3845.                     if (pData->vScroll)
  3846.                         {
  3847.                         DoKeyPageDown(pWindow, pData, processPageControls);
  3848.                         passToObject = false;
  3849.                         }
  3850.                     break;
  3851.                             
  3852.                 case kUpArrow:        // scroll bar up arrow
  3853.                     isMotionKey = true;
  3854.                     if ( (pData->vScroll) && (!pData->pKeyEvent) )
  3855.                         {
  3856.                         if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3857.                             DoKeyPageUp(pWindow, pData, processPageControls);
  3858.                         else
  3859.                             VActionProc(pData->vScroll, kControlUpButtonPart);
  3860.                         passToObject = false;
  3861.                         }
  3862.                     break;
  3863.                     
  3864.                 case kDownArrow:    // scroll bar down arrow
  3865.                     isMotionKey = true;
  3866.                     if ( (pData->vScroll) && (!pData->pKeyEvent) )
  3867.                         {
  3868.                         if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3869.                             DoKeyPageDown(pWindow, pData, processPageControls);
  3870.                         else
  3871.                             VActionProc(pData->vScroll, kControlDownButtonPart);
  3872.                         passToObject = false;
  3873.                         }
  3874.                     break;
  3875.         
  3876.                 case kLeftArrow:    // scroll bar left arrow
  3877.                     isMotionKey = true;
  3878.                     if ( (pData->hScroll) && (!pData->pKeyEvent) )
  3879.                         {
  3880.                         if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3881.                             HActionProc(pData->hScroll, kControlPageUpPart);
  3882.                         else
  3883.                             HActionProc(pData->hScroll, kControlUpButtonPart);
  3884.                         passToObject = false;
  3885.                         }
  3886.                     break;
  3887.                     
  3888.                 case kRightArrow:    // scroll bar right arrow
  3889.                     isMotionKey = true;
  3890.                     if ( (pData->hScroll) && (!pData->pKeyEvent) )
  3891.                         {
  3892.                         if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3893.                             HActionProc(pData->hScroll, kControlPageDownPart);
  3894.                         else
  3895.                             HActionProc(pData->hScroll, kControlDownButtonPart);
  3896.                         passToObject = false;
  3897.                         }
  3898.                     break;
  3899.                     }
  3900.         
  3901.             if (passToObject)
  3902.                 anErr = (*(pData->pKeyEvent)) (pWindow, pData, pEvent, isMotionKey);
  3903.             else
  3904.                 {
  3905.                 if ( (pData->documentAcceptsText == false) && !( pEvent->modifiers & cmdKey ) && !(isMotionKey) )
  3906.                     anErr = eDocumentNotModifiable;
  3907.                 }
  3908.             }
  3909.  
  3910.         if ( (anErr != noErr) && (anErr != eActionAlreadyHandled) )
  3911.             ConductErrorDialog(anErr, cTypingCommand, ok);
  3912.             
  3913.         } // (menuResult == 0)
  3914.     
  3915.         
  3916.     return anErr;
  3917.     
  3918. } // DoKeyEvent
  3919.  
  3920. // --------------------------------------------------------------------------------------------------------------
  3921. #pragma segment Main
  3922.  
  3923. static OSErr DoAdjustCursor(WindowRef pWindow)
  3924. {
  3925.     OSErr        anErr = noErr;
  3926.     Point        mouse;
  3927.     Boolean        didAdjust = false;
  3928.     
  3929.     if (pWindow)
  3930.         {
  3931.         // not one of our windows?  don't do anything
  3932.         if (GetWindowKind(pWindow) != userKind)
  3933.             didAdjust = true;
  3934.             
  3935.         SetPort((GrafPtr) GetWindowPort(pWindow));
  3936.         
  3937.         if ( (!didAdjust) && (gMachineInfo.haveTSM) )
  3938.             {
  3939.             GetMouse(&mouse);
  3940.             LocalToGlobal(&mouse);
  3941.             if (SetTSMCursor(mouse))
  3942.                 didAdjust = true;
  3943.             }
  3944.             
  3945.         if (!didAdjust)
  3946.             {
  3947.             WindowDataPtr    pData = GetWindowInfo(pWindow);
  3948.             RgnHandle        content = NewRgn();
  3949.             Point            globalMouse;
  3950.             
  3951.             GetMouse(&mouse);
  3952.             globalMouse = mouse;
  3953.             LocalToGlobal(&globalMouse);
  3954.             
  3955.             GetWindowContentRgn(pWindow, content);
  3956.             if ((pData) && (PtInRgn(globalMouse, content)) && (PtInRect(mouse, &pData->contentRect)))
  3957.                 {
  3958.                 Rect            tempRect;
  3959.                 
  3960.                 tempRect = pData->contentRect;
  3961.                 LocalToGlobal(&TopLeft(tempRect));
  3962.                 LocalToGlobal(&BotRight(tempRect));
  3963.                 
  3964.                 if (pData->pAdjustCursor)
  3965.                     anErr = (*(pData->pAdjustCursor)) (pWindow, pData, &mouse, &tempRect);
  3966.     
  3967.                 RectRgn(gCursorRgn, &tempRect);
  3968.                 }
  3969.             DisposeRgn(content);
  3970.             }
  3971.         else
  3972.             anErr = eActionAlreadyHandled;
  3973.         }
  3974.     
  3975.     // nobody set the cursor, we do it ourselves
  3976.     if (anErr != eActionAlreadyHandled)
  3977.         SetCursor(&qd.arrow);
  3978.         
  3979.     return anErr;
  3980.     
  3981. } // DoAdjustCursor
  3982.  
  3983. // --------------------------------------------------------------------------------------------------------------
  3984. #pragma segment Main
  3985.  
  3986. static long DetermineWaitTime(WindowRef pWindow)
  3987. {
  3988.     long    waitTime = kMaxWaitTime;
  3989.     
  3990.     while (pWindow)
  3991.         {
  3992.         long            newWaitTime;
  3993.         WindowDataPtr    pData = GetWindowInfo(pWindow);
  3994.         
  3995.         if ((pData) && (pData->pCalculateIdleTime))
  3996.             newWaitTime = (*(pData->pCalculateIdleTime)) (pWindow, pData);
  3997.         else
  3998.             newWaitTime = kMaxWaitTime;
  3999.         
  4000.         if (newWaitTime < waitTime)
  4001.             waitTime = newWaitTime;
  4002.             
  4003.         pWindow = GetNextWindow(pWindow);
  4004.         }
  4005.     
  4006.     return(waitTime);
  4007.     
  4008. } // DetermineWaitTime
  4009.  
  4010. // --------------------------------------------------------------------------------------------------------------
  4011. #pragma segment Main
  4012.  
  4013. void HandleEvent(EventRecord * pEvent)
  4014. {
  4015.     WindowRef pWindow = FrontWindow();
  4016.     
  4017.     switch (pEvent->what)
  4018.         {
  4019.         case kHighLevelEvent:
  4020.             AEProcessAppleEvent(pEvent);
  4021.             break;
  4022.             
  4023.         case osEvt:
  4024.             switch ((pEvent->message >> 24) & 0xFF) /* high byte of message */
  4025.                 {        
  4026.                 case mouseMovedMessage:
  4027.                     DoAdjustCursor(pWindow);
  4028.                     break;
  4029.                     
  4030.                 case suspendResumeMessage:        /* suspend/resume is also an activate/deactivate */
  4031.                     gMachineInfo.amInBackground = (pEvent->message & 1) == 0;
  4032.                     if (pWindow)
  4033.                         DoActivate(pWindow, !gMachineInfo.amInBackground);
  4034.                         
  4035.                     // on resume, we must call GXUpdateJob for all active jobs in
  4036.                     // order to get instant activation of extensions and to properly
  4037.                     // handle potential shifting of driver names
  4038.                     if ( (gMachineInfo.haveGX) && (!gMachineInfo.amInBackground) )
  4039.                         {
  4040.                         WindowRef    walkWindows = pWindow;
  4041.                         
  4042.                         while (walkWindows)
  4043.                             {
  4044.                             WindowDataPtr    pData = GetWindowInfo(walkWindows);
  4045.                             if (pData)
  4046.                                 {
  4047.                                 gxJob    theJob = pData->hPrint;
  4048.                                 if (theJob)
  4049.                                     {
  4050.                                     GXUpdateJob(theJob);
  4051.                                     }
  4052.                                 }
  4053.                             walkWindows = GetNextWindow(walkWindows);
  4054.                             }
  4055.                         }
  4056.                     break;
  4057.                 }
  4058.             break;
  4059.             
  4060.         case activateEvt:
  4061.             pWindow = (WindowRef) pEvent->message;
  4062.             DoActivate(pWindow, (pEvent->modifiers & activeFlag) != 0);
  4063.             break;
  4064.                             
  4065.         // disk inserted events must be handled, or uninitialized floppies 
  4066.         // won't be recognized.
  4067.         case diskEvt:
  4068.             if ( HiWord(pEvent->message) != noErr ) 
  4069.                 {
  4070.                 Point    where;
  4071.             
  4072.                 SetPt(&where, 70, 50);
  4073.                 ShowCursor();
  4074.                 (void) DIBadMount(where, pEvent->message);
  4075.                 }        
  4076.             break;
  4077.                 
  4078.         case mouseUp:
  4079.             break;
  4080.             
  4081.         case mouseDown:
  4082.             {
  4083.             short part = FindWindow(pEvent->where, &pWindow);                    
  4084.             
  4085.             switch ( part ) 
  4086.                 {
  4087.                 case inContent:
  4088.                     if (pWindow != FrontWindow())
  4089.                         SelectWindow(pWindow);
  4090.                     else
  4091.                         DoContentClick(pWindow);
  4092.                     break;
  4093.                     
  4094.                 case inGoAway:
  4095.                     if (TrackGoAway(pWindow, pEvent->where) )
  4096.                         DoCommand(pWindow, cClose, 0);
  4097.                     break;
  4098.                     
  4099.                 case inGrow:
  4100.                     DoGrowWindow(pWindow, pEvent);
  4101.                     break;
  4102.                     
  4103.                 case inZoomIn:
  4104.                 case inZoomOut:
  4105.                     if ( TrackBox(pWindow, pEvent->where, part) )
  4106.                         DoZoomWindow(pWindow, part);
  4107.                     break;
  4108.                     
  4109.                 case inDrag:
  4110.                     {
  4111.                     WindowDataPtr    pData = GetWindowInfo(pWindow);
  4112.                     
  4113.                     if ( (pData) && (pData->dragWindowAligned) )
  4114.                         DragAlignedWindow((WindowPtr) pWindow, pEvent->where, &qd.screenBits.bounds, nil, nil);
  4115.                     else
  4116.                         DragWindow(pWindow, pEvent->where, &qd.screenBits.bounds);
  4117.                     }
  4118.                     break;
  4119.                     
  4120.                 case inMenuBar:                /* process a mouse menu command (if any) */
  4121.                     {
  4122.                     long    menuResult;
  4123.                     
  4124.                     // force these threads to run to completion so the
  4125.                     // contents of the menus are fully initialized
  4126.                     
  4127.                     if (gFontThread != kNoThreadID)
  4128.                         {
  4129.                         gDontYield = true;
  4130.                         SetThreadState(gFontThread, kReadyThreadState, gFontThread);
  4131.                         YieldToThread(gFontThread);
  4132.                         gDontYield = false;
  4133.                         }
  4134.                     if (gAGThread != kNoThreadID)
  4135.                         {
  4136.                         gDontYield = true;
  4137.                         SetThreadState(gAGThread, kReadyThreadState, gAGThread);
  4138.                         YieldToThread(gAGThread);
  4139.                         gDontYield = false;
  4140.                         }
  4141.                     
  4142.                     pWindow = FrontWindow();
  4143.                     AdjustMenus(pWindow, true, false);
  4144.                     menuResult = MenuSelect(pEvent->where);
  4145.                     if ( (gMachineInfo.haveTSM) && (TSMMenuSelect(menuResult)) )
  4146.                         HiliteMenu(0);
  4147.                     else
  4148.                         DoMenuCommand(pWindow, menuResult);
  4149.                     }
  4150.                     break;
  4151.                     
  4152.                 case inSysWindow:            /* let the system handle the mouseDown */
  4153.                     SystemClick(pEvent, pWindow);
  4154.                     break;
  4155.                     
  4156.                 } // switch(part)
  4157.             }
  4158.             break;
  4159.             
  4160.         case keyDown:
  4161.         case autoKey:                        /* check for menukey equivalents */
  4162.             DoKeyEvent(pWindow, pEvent, true);
  4163.             break;
  4164.             
  4165.         case updateEvt:
  4166.             pWindow = (WindowRef) pEvent->message;
  4167.             DoUpdateWindow(pWindow);
  4168.             break;
  4169.  
  4170.         } // switch (pEvent->what)
  4171.     
  4172. } // HandleEvent
  4173.  
  4174. // --------------------------------------------------------------------------------------------------------------
  4175. #pragma segment Main
  4176.  
  4177. static OSErr    DoEventLoop(void)
  4178. {
  4179.     OSErr        anErr = noErr;
  4180.     Boolean        gotEvent;
  4181.     Boolean        trueGotEvent;
  4182.     WindowRef    pWindow;
  4183.     
  4184.     do     {
  4185.         pWindow = LMGetFirstWindow();        // walk all of our windows, even invisible ones
  4186.         
  4187.         DoAdjustCursor(pWindow);
  4188.         gotEvent = WaitNextEvent(everyEvent, &gEvent, DetermineWaitTime(pWindow), gCursorRgn);
  4189.         trueGotEvent = gotEvent;
  4190.  
  4191.         // WNE may close the window if it's owned by some silly extension.
  4192.         pWindow = LMGetFirstWindow();        
  4193.         
  4194.         // let text services handle the event first if it wishes to do so
  4195.         if ( gMachineInfo.haveTSM )
  4196.             {
  4197.             ScriptCode    keyboardScript;
  4198.             WindowRef    theFront = FrontWindow();
  4199.             
  4200.             if (theFront)
  4201.                 {
  4202.                 SetPort((GrafPtr) GetWindowPort(theFront));
  4203.                 
  4204.                 keyboardScript = GetScriptManagerVariable(smKeyScript);
  4205.                 if (FontToScript(qd.thePort->txFont) != keyboardScript)
  4206.                     TextFont(GetScriptVariable(keyboardScript, smScriptAppFond));
  4207.                 }
  4208.             
  4209.             if (TSMEvent(&gEvent))
  4210.                 gotEvent = false;
  4211.             }
  4212.             
  4213.         // let all windows filter this event, and get time if they wish to
  4214.         while (pWindow)
  4215.             {
  4216.             WindowDataPtr    pData = GetWindowInfo(pWindow);
  4217.             Boolean            finishedEvent = false;
  4218.             
  4219.             // help manager for the front window
  4220.             if ( (pWindow == FrontWindow()) && (pData) && (!gMachineInfo.amInBackground) && (HMGetBalloons()) )
  4221.                 {
  4222.                 Point    theMouse, tipLocation;
  4223.                 short    newBalloon = iNoBalloon;
  4224.                 Rect    tempRect;
  4225.                 
  4226.                 // find out where the mouse is                
  4227.                 SetPort((GrafPtr) GetWindowPort(pWindow));
  4228.                 GetMouse(&theMouse);
  4229.                 
  4230.                 // and only do something if we are within the window itself
  4231.                 if (PtInRect(theMouse, &GetWindowPort(pWindow)->portRect))
  4232.                     {
  4233.                     // is it in the vertical scroll bar?
  4234.                     if (pData->vScroll)
  4235.                         {
  4236.                         tempRect = (**(pData->vScroll)).contrlRect;
  4237.                         if (PtInRect(theMouse, &tempRect))
  4238.                             {
  4239.                             newBalloon = iHelpActiveScroll;
  4240.                             if (GetControlMinimum(pData->vScroll) == GetControlMaximum(pData->vScroll))
  4241.                                 newBalloon = iHelpDimVertScroll;
  4242.                             tipLocation.h = tempRect.right - kFromBottomTipOffset;
  4243.                             tipLocation.v = tempRect.bottom - kFromBottomTipOffset;
  4244.                             }
  4245.                         }
  4246.                         
  4247.                     // is it in the horizontal scroll bar?
  4248.                     if (pData->hScroll)
  4249.                         {
  4250.                         tempRect = (**(pData->hScroll)).contrlRect;
  4251.                         if (PtInRect(theMouse, &tempRect))
  4252.                             {
  4253.                             newBalloon = iHelpActiveScroll;
  4254.                             if (GetControlMinimum(pData->hScroll) == GetControlMaximum(pData->hScroll))
  4255.                                 newBalloon = iHelpDimHorizScroll;
  4256.                             tipLocation.h = tempRect.right - kFromBottomTipOffset;
  4257.                             tipLocation.v = tempRect.bottom - kFromBottomTipOffset;
  4258.                             }
  4259.                         }
  4260.                     
  4261.                     // is it in the grow box?
  4262.                     if (pData->hasGrow)
  4263.                         {
  4264.                         CalculateGrowIcon(pData, &tempRect);
  4265.                         if (PtInRect(theMouse, &tempRect))
  4266.                             {
  4267.                             newBalloon = iHelpGrowBox;
  4268.                             tipLocation.h = tempRect.right - kFromBottomTipOffset;
  4269.                             tipLocation.v = tempRect.bottom - kFromBottomTipOffset;
  4270.                             }
  4271.                         }
  4272.                     
  4273.                     // none of the above, must be the content
  4274.                     if (newBalloon == iNoBalloon)
  4275.                         {
  4276.                         newBalloon = iHelpGenericContent;
  4277.                         tempRect = pData->contentRect;
  4278.                         if (pData->pGetBalloon)
  4279.                             (*(pData->pGetBalloon)) (pWindow, pData, &theMouse, &newBalloon, &tempRect);
  4280.                             
  4281.                         tipLocation.h = tempRect.left + kFromTopTipOffset;
  4282.                         tipLocation.v = tempRect.top + kFromTopTipOffset;
  4283.                         }
  4284.                         
  4285.                     // show our new balloon, or remove the old one
  4286.                     if (newBalloon != iNoBalloon)
  4287.                         {
  4288.                         if ( (gMachineInfo.lastBalloonIndex != newBalloon) || (!HMIsBalloon()) )
  4289.                             {
  4290.                             HMMessageRecord    message;
  4291.                             
  4292.                             if (newBalloon != iDidTheBalloon)
  4293.                                 {
  4294.                                 message.hmmHelpType = khmmString;
  4295.                                 GetIndString(message.u.hmmString, kWindowHelpID, newBalloon);
  4296.                                 LocalToGlobal(&tipLocation);
  4297.                                 (void) HMShowBalloon(&message, tipLocation, nil, nil, 0, kDefaultBalloonVariant, 0);
  4298.                                 }
  4299.                             gMachineInfo.lastBalloonIndex = newBalloon;
  4300.                             }
  4301.                         }
  4302.                     else
  4303.                         HMRemoveBalloon();
  4304.                     }
  4305.                     
  4306.                     
  4307.                 }
  4308.                 
  4309.             // if we hit a window we know about, then do filtering
  4310.             if (pData)
  4311.                 {
  4312.                 if (pData->pFilterEvent)
  4313.                     finishedEvent = (*(pData->pFilterEvent)) (pWindow, pData, &gEvent);
  4314.                 }
  4315.  
  4316.             // if filtering indicates complete handling of event, then stop, and
  4317.             // do no regular processing.
  4318.             if (finishedEvent)
  4319.                 {
  4320.                 gotEvent = false;
  4321.                 pWindow = nil;
  4322.                 }
  4323.             else
  4324.                 pWindow = GetNextWindow(pWindow);
  4325.             }
  4326.             
  4327.         if (gotEvent)
  4328.             HandleEvent(&gEvent);
  4329.             
  4330.         // close request?
  4331.         if (gAllDone)
  4332.             {
  4333.             pWindow = FrontWindow();
  4334.             while ((gAllDone) && (pWindow) )
  4335.                 {
  4336.                 WindowRef    nextWindow = GetNextWindow(pWindow);
  4337.                 OSErr        closeError = DoCloseWindow(pWindow, true);
  4338.                 
  4339.                 // window didn't close?  then don't quit
  4340.                 if (pWindow == FrontWindow())
  4341.                     gAllDone = false;
  4342.                     
  4343.                 // something bad happened, then don't quit
  4344.                 if ( (closeError != noErr) /* && (closeError != eUserCanceled) */ )
  4345.                     gAllDone = false;
  4346.                     
  4347.                 pWindow = nextWindow;
  4348.                 }
  4349.             }
  4350.         
  4351.         // our threads are low-priority, so we only give time to them on idle
  4352.         if (gMachineInfo.haveThreads && !trueGotEvent && !gAllDone)
  4353.             YieldToAnyThread();
  4354.         
  4355.         } while (!gAllDone);
  4356.         
  4357.     return anErr;
  4358.     
  4359. } // DoEventLoop
  4360.  
  4361.  
  4362. // --------------------------------------------------------------------------------------------------------------
  4363. // DRAG MANAGEMENT GLOBAL SUPPORT ROUTINES
  4364. // --------------------------------------------------------------------------------------------------------------
  4365.  
  4366. // Globals for our drag handlers
  4367.  
  4368. Boolean                gCanAccept;                // if we can receive the item(s) being dragged
  4369.  
  4370. // --------------------------------------------------------------------------------------------------------------
  4371. #pragma segment Drag
  4372.  
  4373. static pascal OSErr GlobalTrackingHandler(short message, WindowRef pWindow, void *handlerRefCon, DragReference theDragRef)
  4374. {
  4375.     #pragma unused(handlerRefCon)
  4376.  
  4377.     WindowDataPtr pData = GetWindowInfo(pWindow);
  4378.  
  4379.     // Call the tracking handler associated with this type of window. Only allow messages referencing
  4380.     // a specific window to be passed to the handler.
  4381.  
  4382.     if (pData)
  4383.         {    
  4384.         if (pData->pDragTracking)
  4385.             return ((*(pData->pDragTracking)) (pWindow, pData, theDragRef, message));
  4386.         }
  4387.     
  4388.     return noErr;
  4389.  
  4390. } // GlobalTrackingHandler
  4391.  
  4392. DragTrackingHandlerUPP gGlobalTrackingHandler;
  4393.  
  4394. // --------------------------------------------------------------------------------------------------------------
  4395. #pragma segment Drag
  4396.  
  4397. static pascal OSErr GlobalReceiveHandler(WindowRef pWindow, void *handlerRefCon, DragReference theDragRef)
  4398. {
  4399.     #pragma unused(handlerRefCon)
  4400.  
  4401.     WindowDataPtr pData = GetWindowInfo(pWindow);
  4402.     
  4403.     if (pData)
  4404.         {
  4405.         if (pData->pDragTracking)
  4406.             return ((*(pData->pDragReceive)) (pWindow, pData, theDragRef));
  4407.         }
  4408.  
  4409.     return noErr;
  4410.  
  4411. } // GlobalReceiveHandler
  4412.  
  4413. DragReceiveHandlerUPP gGlobalReceiveHandler;
  4414.  
  4415. // --------------------------------------------------------------------------------------------------------------
  4416. //
  4417. // IsOnlyThisFlavor - Given a DragReference and a FlavorType, we iterate through the drag items to determine if
  4418. //                      all are of flavor theType. If this is so, we return true. If any of the items are not
  4419. //                      theType, we return false, indicating that we should not accept the drag.
  4420. //
  4421. #pragma segment Drag
  4422.  
  4423. Boolean IsOnlyThisFlavor(DragReference theDragRef, FlavorType theType)
  4424. {
  4425.     unsigned short    items, index;
  4426.     FlavorFlags        theFlags;
  4427.     ItemReference    itemID;
  4428.     OSErr            anErr = noErr;
  4429.  
  4430.     CountDragItems(theDragRef, &items);
  4431.     
  4432.     for(index = 1; index <= items; index++)
  4433.         {
  4434.         GetDragItemReferenceNumber(theDragRef, index, &itemID);
  4435.  
  4436.         anErr = GetFlavorFlags(theDragRef, itemID, theType, &theFlags);
  4437.         if(anErr == noErr)
  4438.             continue;    // it's okay, this flavor is cool
  4439.  
  4440.         return false;    // this item has at least one flavor we don't like
  4441.         }
  4442.  
  4443.     return true;        // all flavors in this item were cool
  4444.  
  4445. } // IsOnlyThisFlavor
  4446.  
  4447. // --------------------------------------------------------------------------------------------------------------
  4448. //
  4449. // IsDropInFinderTrash - Returns true if the given dropLocation AEDesc is a descriptor of the Finder's Trash.
  4450. //
  4451. #pragma segment Drag
  4452.  
  4453. Boolean IsDropInFinderTrash(AEDesc *dropLocation)
  4454. {
  4455.     OSErr            result;
  4456.     AEDesc            dropSpec;
  4457.     FSSpec            *theSpec;
  4458.     CInfoPBRec        thePB;
  4459.     short            trashVRefNum;
  4460.     long            trashDirID;
  4461.  
  4462.     //    Coerce the dropLocation descriptor into an FSSpec. If there's no dropLocation or
  4463.     //    it can't be coerced into an FSSpec, then it couldn't have been the Trash.
  4464.  
  4465.     if ((dropLocation->descriptorType != typeNull) &&
  4466.         (AECoerceDesc(dropLocation, typeFSS, &dropSpec) == noErr)) 
  4467.         {
  4468.         unsigned char flags = HGetState(dropSpec.dataHandle);
  4469.         
  4470.         HLock(dropSpec.dataHandle);
  4471.         theSpec = (FSSpec *) *dropSpec.dataHandle;
  4472.  
  4473.         //    Get the directory ID of the given dropLocation object.
  4474.  
  4475.         thePB.dirInfo.ioCompletion = 0L;
  4476.         thePB.dirInfo.ioNamePtr = (StringPtr) &theSpec->name;
  4477.         thePB.dirInfo.ioVRefNum = theSpec->vRefNum;
  4478.         thePB.dirInfo.ioFDirIndex = 0;
  4479.         thePB.dirInfo.ioDrDirID = theSpec->parID;
  4480.  
  4481.         result = PBGetCatInfoSync(&thePB);
  4482.  
  4483.         HSetState(dropSpec.dataHandle, flags);
  4484.         AEDisposeDesc(&dropSpec);
  4485.  
  4486.         if (result != noErr)
  4487.             return false;
  4488.  
  4489.         //    If the result is not a directory, it must not be the Trash.
  4490.  
  4491.         if (!(thePB.dirInfo.ioFlAttrib & (1 << 4)))
  4492.             return false;
  4493.  
  4494.         //    Get information about the Trash folder.
  4495.  
  4496.         FindFolder(theSpec->vRefNum, kTrashFolderType, kCreateFolder, &trashVRefNum, &trashDirID);
  4497.  
  4498.         //    If the directory ID of the dropLocation object is the same as the directory ID
  4499.         //    returned by FindFolder, then the drop must have occurred into the Trash.
  4500.  
  4501.         if (thePB.dirInfo.ioDrDirID == trashDirID)
  4502.             return true;
  4503.         }
  4504.  
  4505.     return false;
  4506.  
  4507. } // IsDropInFinderTrash
  4508.  
  4509. // --------------------------------------------------------------------------------------------------------------
  4510. // APPLE EVENT SUPPORT ROUTINES
  4511. // --------------------------------------------------------------------------------------------------------------
  4512. #pragma segment Main
  4513.  
  4514. static OSErr    MissingParameterCheck(
  4515.     AppleEvent     *inputEvent)
  4516. /*
  4517.     This routine checks an input AppleEvent for the missing keyword.
  4518.     If the missing keyword is found, that means that some required
  4519.     parameters were missing (ie, an error). 
  4520.     
  4521.     However, if the missing keyword isn't found, that means that we aren't missing 
  4522.     any required parameters (that is to say, all REQUIRED parameters were supplied
  4523.     by the person who created the event).
  4524.     
  4525.     SOME DAY, THE ABOVE COMMENT WILL MAKE SENSE TO YOU.  IT STILL DOESN'T
  4526.     TO ME AND I WAS THE ONE WHO WROTE IT.
  4527. */
  4528. {
  4529.     OSErr        anErr;
  4530.     AEKeyword    missingKeyword;
  4531.     DescType    ignoredActualType;
  4532.     Size        ignoredActualSize;
  4533.     
  4534.     anErr = AEGetAttributePtr(
  4535.         inputEvent, 
  4536.         keyMissedKeywordAttr,
  4537.         typeWildCard,
  4538.         &ignoredActualType,
  4539.         (Ptr) &missingKeyword,
  4540.         sizeof(AEKeyword),
  4541.         &ignoredActualSize);
  4542.             
  4543.     if (anErr == noErr)
  4544.         anErr = errAEParamMissed;
  4545.     else
  4546.         if (anErr == errAEDescNotFound)
  4547.             anErr = noErr;
  4548.         
  4549.     return anErr;
  4550.     
  4551. } // MissingParameterCheck
  4552.  
  4553. // --------------------------------------------------------------------------------------------------------------
  4554. // Globals for our handlers
  4555. Boolean gQuitAfterPrint = true;
  4556.  
  4557. // --------------------------------------------------------------------------------------------------------------
  4558. #pragma segment Main
  4559.  
  4560. static pascal OSErr    DoOpenApp(
  4561.     AppleEvent     *inputEvent,
  4562.     AppleEvent     *outputEvent,
  4563.     long        handlerRefCon)
  4564. {
  4565. #pragma unused (outputEvent, handlerRefCon)
  4566.  
  4567.     DoCommand(nil, cNew, 0);
  4568.     gQuitAfterPrint = false;
  4569.     
  4570.     // so that the initial document opens more quickly, we don't start
  4571.     // the threads until we get an OpenApp or OpenDocument AppleEvent
  4572.     if (gStarterThread != kNoThreadID)
  4573.         SetThreadState(gStarterThread, kReadyThreadState, gStarterThread);
  4574.     
  4575.     return(MissingParameterCheck(inputEvent));
  4576.     
  4577. } // DoAppOpen
  4578.  
  4579. // --------------------------------------------------------------------------------------------------------------
  4580. #pragma segment Main
  4581.  
  4582. static pascal OSErr    DoQuitApp(
  4583.     AppleEvent     *inputEvent,
  4584.     AppleEvent     *outputEvent,
  4585.     long        handlerRefCon)
  4586. {
  4587. #pragma unused (outputEvent, handlerRefCon)
  4588.  
  4589.     DoCommand(nil, cQuit, 0);
  4590.  
  4591.     return(MissingParameterCheck(inputEvent));
  4592.     
  4593. } // DoQuitApp
  4594.  
  4595. // --------------------------------------------------------------------------------------------------------------
  4596. #pragma segment Main
  4597.  
  4598. static pascal OSErr    DoOpenOrPrint(
  4599.     AppleEvent     *inputEvent,
  4600.     StringPtr    pPrinterName)    // nil == 0, zero length == print to default, other == printer name
  4601. {
  4602.  
  4603.     OSErr        anErr, anErr2;
  4604.     AEDescList    docList;                // list of docs passed in
  4605.     long        index, itemsInList;
  4606.     void*        hPrint;
  4607.     Boolean        wasAlreadyOpen;
  4608.     
  4609.     anErr = AEGetParamDesc( inputEvent, keyDirectObject, typeAEList, &docList);
  4610.     nrequire(anErr, GetFileList);
  4611.  
  4612.     anErr = AECountItems( &docList, &itemsInList);            // how many files passed in
  4613.     nrequire(anErr, CountDocs);
  4614.     for (index = 1; index <= itemsInList; index++)            // handle each file passed in
  4615.         {    
  4616.         AEKeyword    keywd;
  4617.         DescType    returnedType;
  4618.         Size        actualSize;
  4619.         FSSpec        theFSS;    
  4620.  
  4621.         anErr = AEGetNthPtr( &docList, index, typeFSS, &keywd, &returnedType,    // get file's info
  4622.                             (Ptr)(&theFSS), sizeof(theFSS), &actualSize);
  4623.         nrequire(anErr, AEGetNthPtr);
  4624.         
  4625.         {
  4626.         FInfo    theFileInfo;
  4627.         
  4628.         anErr = FSpGetFInfo(&theFSS, &theFileInfo);
  4629.         if (anErr == noErr)
  4630.             anErr = DetermineWindowTypeOrOpen(&theFSS, theFileInfo.fdType, nil, nil, &wasAlreadyOpen);
  4631.             
  4632.         if (anErr == eDocumentWrongKind)
  4633.             {
  4634.             if (pPrinterName)
  4635.                 ConductErrorDialog(anErr, cPrint, cancel);
  4636.             else
  4637.                 ConductErrorDialog(anErr, cOpen, cancel);
  4638.  
  4639.             anErr = noErr;
  4640.             break;
  4641.             }
  4642.             
  4643.         nrequire(anErr, DetermineWindowTypeOrOpen);
  4644.         }
  4645.         
  4646.         if (pPrinterName)
  4647.             {
  4648.             WindowRef        pWindow = FrontWindow();
  4649.             WindowDataPtr    pData = GetWindowInfo(pWindow);
  4650.             
  4651.             if (pData->pPrintPage)
  4652.                 {
  4653.                 if (index == 1)
  4654.                     {
  4655.                     anErr = DoPrintSetup(pWindow, pPrinterName);
  4656.                     if (anErr == noErr)
  4657.                         hPrint = pData->hPrint;
  4658.                     }
  4659.                     
  4660.                 if (anErr == noErr)
  4661.                     anErr = DoPrint(pWindow, hPrint, false);
  4662.                     
  4663.                 if (index != itemsInList)
  4664.                     pData->hPrint = nil;
  4665.                 }
  4666.             
  4667.             if (!wasAlreadyOpen)
  4668.                 DoCloseWindow(pWindow, false);
  4669.  
  4670.             if (anErr != noErr)
  4671.                 break;
  4672.             }
  4673.         }
  4674.  
  4675.     // finally, make sure we didn't miss any parameters
  4676.     anErr2 = MissingParameterCheck(inputEvent);
  4677.     if (anErr == noErr)
  4678.         anErr = anErr2;
  4679.         
  4680. // FALL THROUGH EXCEPTION HANDLING
  4681. DetermineWindowTypeOrOpen:
  4682. AEGetNthPtr:
  4683. CountDocs:
  4684.     // done with doc list
  4685.     (void) AEDisposeDesc( &docList);                        
  4686.     
  4687. GetFileList:
  4688.  
  4689.     // don't report cancels from prints
  4690.     if (pPrinterName)
  4691.         {
  4692.         if ( (anErr == iPrAbort) || (anErr == gxPrUserAbortErr) )
  4693.             anErr = noErr;
  4694.         }
  4695.     
  4696.     if ( (anErr != noErr) && (anErr != eActionAlreadyHandled) && (anErr != eUserCanceled) )
  4697.         {
  4698.         if (pPrinterName)
  4699.             ConductErrorDialog(anErr, cPrint, cancel);
  4700.         else
  4701.             ConductErrorDialog(anErr, cOpen, cancel);
  4702.         }
  4703.         
  4704.     return anErr;
  4705.     
  4706. } // DoOpenOrPrint
  4707.  
  4708. // --------------------------------------------------------------------------------------------------------------
  4709. #pragma segment Main
  4710.  
  4711. static pascal OSErr    DoOpenDocument(
  4712.     AppleEvent     *inputEvent,
  4713.     AppleEvent     *outputEvent,
  4714.     long        handlerRefCon)
  4715. {
  4716. #pragma unused (outputEvent, handlerRefCon)
  4717.  
  4718.     OSErr        anErr;
  4719.     
  4720.     if (IsCommandEnabled(cOpen))
  4721.         {
  4722.         gQuitAfterPrint = false;
  4723.         anErr = DoOpenOrPrint(inputEvent, nil);
  4724.         }
  4725.     else
  4726.         {
  4727.         anErr = errAEEventNotHandled;
  4728.         ConductErrorDialog(anErr, cOpen, cancel);
  4729.         }
  4730.         
  4731.     // so that the initial document opens more quickly, we don't start
  4732.     // the threads until we get an OpenApp or OpenDocument AppleEvent
  4733.     if (gStarterThread != kNoThreadID)
  4734.         SetThreadState(gStarterThread, kReadyThreadState, gStarterThread);
  4735.     
  4736.     return anErr;
  4737.     
  4738. } // DoOpenDocument
  4739.  
  4740. // --------------------------------------------------------------------------------------------------------------
  4741. #pragma segment Main
  4742.  
  4743. static pascal OSErr    DoPrintDocument(
  4744.     AppleEvent     *inputEvent,
  4745.     AppleEvent     *outputEvent,
  4746.     long        handlerRefCon)
  4747. {
  4748. #pragma unused (outputEvent, handlerRefCon)
  4749.     OSErr        anErr;
  4750.     FSSpec        printerFSS;
  4751.     AEDescList    dtpList;                // list of docs passed in
  4752.     
  4753.     if (IsCommandEnabled(cOpen))
  4754.         {
  4755.         // try to find out if this doc was dropped onto a printer
  4756.         anErr = AEGetAttributeDesc( inputEvent, keyOptionalKeywordAttr, typeAEList, &dtpList);
  4757.     
  4758.         if (anErr == noErr)                                            // doc dragged to dtp?
  4759.             {
  4760.             AEKeyword    keywd;
  4761.             DescType    returnedType;
  4762.             Size        actualSize;
  4763.     
  4764.             anErr = AEGetNthPtr( &dtpList, 1, typeFSS, &keywd, &returnedType,    // get dtp info
  4765.                             (Ptr)(&printerFSS), sizeof(printerFSS), &actualSize);
  4766.             }
  4767.             
  4768.         // if it wasn't, that's not an error, just print normally
  4769.         if (anErr != noErr)
  4770.             {
  4771.             printerFSS.name[0] = 0;
  4772.             anErr = noErr;
  4773.             }
  4774.             
  4775.         anErr = DoOpenOrPrint(inputEvent, &printerFSS.name[0]);
  4776.         
  4777.         // if we are opened just for printing -- quit afterwards
  4778.         if (gQuitAfterPrint)
  4779.             DoCommand(nil, cQuit, 0);
  4780.         }
  4781.     else
  4782.         {
  4783.         anErr = errAEEventNotHandled;
  4784.         ConductErrorDialog(anErr, cPrint, cancel);
  4785.         }
  4786.         
  4787.     return anErr;
  4788.     
  4789. } // DoPrintDocument
  4790.  
  4791. #if GENERATINGCFM
  4792.     static RoutineDescriptor    gDoOpenAppRD = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, DoOpenApp);
  4793.     static AEEventHandlerUPP    gDoOpenApp = &gDoOpenAppRD;
  4794.  
  4795.     static RoutineDescriptor    gDoQuitAppRD = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, DoQuitApp);
  4796.     static AEEventHandlerUPP    gDoQuitApp = &gDoQuitAppRD;
  4797.  
  4798.     static RoutineDescriptor    gDoOpenDocumentRD = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, DoOpenDocument);
  4799.     static AEEventHandlerUPP    gDoOpenDocument = &gDoOpenDocumentRD;
  4800.  
  4801.     static RoutineDescriptor    gDoPrintDocumentRD = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, DoPrintDocument);
  4802.     static AEEventHandlerUPP    gDoPrintDocument = &gDoPrintDocumentRD;
  4803. #else
  4804.     static AEEventHandlerUPP    gDoOpenApp = (AEEventHandlerUPP) DoOpenApp;
  4805.     static AEEventHandlerUPP    gDoQuitApp = (AEEventHandlerUPP) DoQuitApp;
  4806.     static AEEventHandlerUPP    gDoOpenDocument = (AEEventHandlerUPP) DoOpenDocument;
  4807.     static AEEventHandlerUPP    gDoPrintDocument = (AEEventHandlerUPP) DoPrintDocument;
  4808. #endif
  4809. // --------------------------------------------------------------------------------------------------------------
  4810. #pragma segment Main
  4811.  
  4812. static pascal OSErr SimpleTextCoachHandler(Rect *pRect, Ptr name, long refCon)
  4813. {
  4814. #pragma unused (refCon)
  4815.  
  4816.     OSErr            anErr = noErr;
  4817.     WindowRef        pWindow = FrontWindow();
  4818.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  4819.     
  4820.     if ((pData) && (pData->pGetCoachRectangle))
  4821.         anErr = (*(pData->pGetCoachRectangle)) (pWindow, pData, pRect, name);
  4822.         
  4823.     return(anErr);
  4824.     
  4825. } // SimpleTextCoachHandler
  4826.  
  4827. // --------------------------------------------------------------------------------------------------------------
  4828. // MAIN INITIALIZE/SHUTDOWN/LOOP ROUTINES
  4829. // --------------------------------------------------------------------------------------------------------------
  4830. #pragma segment Main
  4831.  
  4832. static pascal void* StarterThread(void* threadParam)
  4833. {
  4834.     #pragma unused(threadParam)
  4835.     
  4836.     /*
  4837.         All threads, including the starter thread, are initially created
  4838.         in a suspended state. The starter thread is made ready to run when
  4839.         we get an open application or open document event. It runs until
  4840.         there are no activate or update events pending, and then starts the
  4841.         font menu and Apple Guide threads. This gives much better performance
  4842.         for the initial creation and update of a document, because the threads
  4843.         (especially the font thread) chew up a lot of time at first.
  4844.         
  4845.         The starter thread isn't really necessary - we could get the same
  4846.         effect by just checking in the event loop for activate/update events -
  4847.         but hey, it's a lot easier to do it this way, doesn't cost much, and
  4848.         isn't that what threads are for?
  4849.     */
  4850.     
  4851.     for (;;)
  4852.         {
  4853.         EventRecord        er;
  4854.     
  4855.         YieldToAnyThread();
  4856.         
  4857.         if (!EventAvail(activMask | updateMask, &er))
  4858.             {
  4859.             if (gFontThread != kNoThreadID)
  4860.                 SetThreadState(gFontThread, kReadyThreadState, gFontThread);
  4861.             if (gAGThread != kNoThreadID)
  4862.                 SetThreadState(gAGThread, kReadyThreadState, gAGThread);
  4863.             
  4864.             break;
  4865.             }
  4866.         }
  4867.     
  4868.     gStarterThread = kNoThreadID;
  4869.     return NULL;
  4870.     
  4871. } // StarterThread
  4872.  
  4873. // --------------------------------------------------------------------------------------------------------------
  4874. #pragma segment Initialize
  4875.  
  4876. static OSErr CreateThread(ThreadEntryProcPtr pThread, void* threadParam, ThreadID* ptid)
  4877. {
  4878.     OSErr    anErr;
  4879.     
  4880.     anErr = NewThread(kCooperativeThread, pThread, threadParam, 0, kNewSuspend,
  4881.         &gThreadResults, ptid);
  4882.         
  4883.     if (anErr == noErr && gStarterThread == kNoThreadID)
  4884.         {
  4885.         anErr = NewThread(kCooperativeThread, StarterThread, NULL, 0, kNewSuspend,
  4886.             &gThreadResults, &gStarterThread);
  4887.         if (anErr != noErr)
  4888.             DisposeThread(*ptid, &gThreadResults, false);
  4889.             // anErr remains != noErr
  4890.         }
  4891.         
  4892.     return anErr;
  4893. }
  4894.         
  4895. // --------------------------------------------------------------------------------------------------------------
  4896. // must be in Main because it runs in a thread and we don't want the segment unloaded
  4897. // while some other thread is running
  4898. #pragma segment Main
  4899.  
  4900. static OSErr FindSimpleTextGuideFile(FSSpec *pSpec)
  4901. {
  4902.     OSErr    anErr = fnfErr;
  4903.     short    numDBs, index;
  4904.     short    folderIndex;
  4905.     
  4906.     for (folderIndex = 0; folderIndex < 3; ++folderIndex)
  4907.         {
  4908.         switch (folderIndex)
  4909.             {
  4910.             case 0:
  4911.                 FindFolder(-1, kPreferencesFolderType, false, &pSpec->vRefNum, &pSpec->parID);
  4912.                 break;
  4913.  
  4914.             case 1:
  4915.                 FindFolder(-1, kExtensionFolderType, false, &pSpec->vRefNum, &pSpec->parID);
  4916.                 break;
  4917.  
  4918.             case 2:
  4919.                 FindFolder(-1, kSystemFolderType, false, &pSpec->vRefNum, &pSpec->parID);
  4920.                 break;
  4921.             }
  4922.         
  4923.         numDBs = AGFileGetDBCount(pSpec->vRefNum, pSpec->parID, kAGFileDBTypeAny, false);
  4924.         
  4925.         if (!gDontYield)
  4926.             YieldToAnyThread();
  4927.         
  4928.         for (index = 0; index < numDBs; ++index)
  4929.             {
  4930.             if (!gDontYield)
  4931.                 YieldToAnyThread();
  4932.             
  4933.             if (AGFileGetIndDB(pSpec->vRefNum, pSpec->parID, kAGFileDBTypeAny, false, index+1, pSpec) == noErr)
  4934.                 {
  4935.                 OSType    creator;
  4936.                 
  4937.                 if ((AGFileGetHelpMenuAppCreator(pSpec, &creator) == noErr) && (creator == 'ttxt'))
  4938.                     return(noErr);
  4939.                 }
  4940.             }
  4941.         }
  4942.     
  4943.     return(anErr);
  4944.     
  4945. } // FindSimpleTextGuideFile
  4946.  
  4947. // --------------------------------------------------------------------------------------------------------------
  4948. // must be in Main because it runs in a thread and we don't want the segment unloaded
  4949. // while some other thread is running
  4950. #pragma segment Main
  4951.  
  4952. static pascal void* AGThread(void *threadParam)
  4953. {
  4954.     #pragma unused(threadParam)
  4955.     
  4956.     if ( !(AGGetAvailableDBTypes() & kAGDBTypeBitAny) )
  4957.         {
  4958.         // if we find one, add it to the help menu and add the right command
  4959.         if (FindSimpleTextGuideFile(&gAGSpec) == noErr)
  4960.             {
  4961.             MenuHandle    helpMenu;
  4962.                         
  4963.             if (HMGetHelpMenuHandle(&helpMenu) == noErr)
  4964.                 {
  4965.                 Str255    tempString;
  4966.                 short    rnSave;
  4967.                 
  4968.                 AGFileGetDBMenuName(&gAGSpec, tempString);
  4969.                 AppendMenu(helpMenu, tempString);
  4970.                 
  4971.                 // since we're in a separate thread, so other resource file may
  4972.                 // be open in front of SimpleText's resource fork
  4973.                 rnSave = CurResFile();
  4974.                 UseResFile(gApplicationResFile);
  4975.                 
  4976.                 GetIndString(tempString, kMiscStrings, iHelpMenuCommand);
  4977.                 
  4978.                 UseResFile(rnSave);
  4979.                 
  4980.                 if (tempString[0] != 0)
  4981.                     SetItemCmd(helpMenu, CountMItems(helpMenu), tempString[1]);
  4982.                 }
  4983.             }
  4984.         }
  4985.     
  4986.     // if we have a database, then install a coach handler
  4987.     if ( (AGGetAvailableDBTypes() & kAGDBTypeBitAny) )
  4988.         {
  4989.         AGInstallCoachHandler(NewCoachReplyProc(SimpleTextCoachHandler), 0, &gAGCoachRefNum);
  4990.         }
  4991.     
  4992.     gAGThread = kNoThreadID;
  4993.     return NULL;
  4994.     
  4995. } // AGThread
  4996.  
  4997. // --------------------------------------------------------------------------------------------------------------
  4998. #pragma segment Initialize
  4999.  
  5000. static void FindAndInstallSimpleTextGuide(void)
  5001. {
  5002.     OSErr    anErr = fnfErr;
  5003.     
  5004.     if (gMachineInfo.haveThreads)
  5005.         anErr = CreateThread(AGThread, NULL, &gAGThread);
  5006.     
  5007.     if (anErr != noErr)
  5008.         AGThread(NULL);
  5009.         
  5010. } // FindAndinstallSimpleTextGuide
  5011.  
  5012. // --------------------------------------------------------------------------------------------------------------
  5013. // must be in Main because it runs in a thread and we don't want the segment unloaded
  5014. // while some other thread is running
  5015. #pragma segment Main
  5016.  
  5017. static long SortAndAddMenu(MenuHandle menu, Str255 newItem)
  5018. {
  5019.     short    numInMenu = CountMItems(menu);
  5020.     short    i;
  5021.     Str255    oldItem;
  5022.     
  5023.     for (i = 1; i <= numInMenu; ++i)
  5024.         {
  5025.         GetMenuItemText(menu, i, oldItem);
  5026.         switch(IUCompString(newItem, oldItem))
  5027.             {
  5028.             // already in?  Return index
  5029.             case 0:
  5030.                 return(i);
  5031.                 break;
  5032.                 
  5033.             // less than, keep scanning
  5034.             case 1:
  5035.                 break;
  5036.                 
  5037.             // greater than, add back one
  5038.             case -1:
  5039.                 InsertMenuItem(menu, "\pTom Dowdy", i-1);
  5040.                 SetMenuItemText(menu, i, newItem);
  5041.                 return(i);
  5042.                 break;
  5043.             }
  5044.         }
  5045.         
  5046.     // fall off the end?  add at the end
  5047.     InsertMenuItem(menu, "\pTom Dowdy", numInMenu);
  5048.     SetMenuItemText(menu, numInMenu+1, newItem);
  5049.             
  5050.     return(numInMenu+1);
  5051.     
  5052. } // SortAndAddMenu
  5053.  
  5054. // --------------------------------------------------------------------------------------------------------------
  5055. // must be in Main because it runs in a thread and we don't want the segment unloaded
  5056. // while some other thread is running
  5057. #pragma segment Main
  5058.  
  5059. static void AddToGlobalList(gxFont fontID, short fond, Style styleBits)
  5060. {
  5061.     if (!gFontMappingList)
  5062.         gFontMappingList = (FontMappingHandle)NewHandle(0);
  5063.         
  5064.     if (gFontMappingList)
  5065.         {
  5066.         Size            oldSize = GetHandleSize((Handle) gFontMappingList);
  5067.         FontMappingPtr    pList;
  5068.         
  5069.         SetHandleSize((Handle)gFontMappingList, oldSize + sizeof(FontMappingRecord));
  5070.         pList = &(*gFontMappingList)[oldSize/sizeof(FontMappingRecord)];
  5071.         pList->fontID = fontID;
  5072.         pList->qdFont = fond;
  5073.         pList->qdStyle = styleBits;
  5074.         }
  5075.         
  5076. } // AddToGlobalList
  5077.  
  5078. // --------------------------------------------------------------------------------------------------------------
  5079. // must be in Main because it runs in a thread and we don't want the segment unloaded
  5080. // while some other thread is running
  5081. #pragma segment Main
  5082.  
  5083. static void AddEachEntry(Handle fond, short* sp, short entries, gxStyle theStyle, MenuHandle menu, short* pStylesUsed)
  5084. {
  5085.     gxFont            fontID;
  5086.     long            length;
  5087.     short             resID;
  5088.     OSType             resType;
  5089.     Str255             resName;
  5090.     Style            styleBits;
  5091.     
  5092.     GetResInfo(fond, &resID, &resType, resName);
  5093.     
  5094.     for (; entries >= 0; --entries)
  5095.         {   
  5096.         if (*sp == 0)
  5097.             {   
  5098.             styleBits = sp[1];
  5099.  
  5100.             // map the font ID and the style bits for this style into GX space
  5101.             (void)GXConvertQDFont(theStyle, resID, styleBits);
  5102.             
  5103.             if (!gDontYield)
  5104.                 YieldToAnyThread();
  5105.     
  5106.             fontID = GXGetStyleFont(theStyle);
  5107.             if (fontID)
  5108.                 length = GXFindFontName(fontID, gxFamilyFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, nil, nil);
  5109.             else
  5110.                 length = 0;
  5111.                 
  5112.             if (!gDontYield)
  5113.                 YieldToAnyThread();
  5114.     
  5115.             if (length)
  5116.                 {
  5117.                 unsigned char     * name;
  5118.                 short            where;
  5119.                 
  5120.                 name = (unsigned char*) NewPtr(length+1);
  5121.                 if (name)
  5122.                     {
  5123.                     MenuHandle    subMenu;
  5124.                     short        mark;
  5125.  
  5126.                     // add this font to the list we know about
  5127.                     AddToGlobalList(fontID, resID, styleBits);
  5128.  
  5129.                     // find the family name for this font
  5130.                     GXFindFontName(fontID, gxFamilyFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, &name[1], nil);
  5131.                     name[0] = length;
  5132.                     
  5133.                     // add the name if new. Then add sub menus
  5134.                     where = SortAndAddMenu(menu, name);
  5135.                     
  5136.                     // do we already have a sub menu?  Or does this font have multiple styles?
  5137.                     GetItemMark(menu, where, &mark);
  5138.                     if ( (mark != 0) || (GXFindFonts(fontID, 0, 0, 0, 0, 0, nil, 1, gxSelectToEnd, nil) > 1) )
  5139.                         {
  5140.                         if (!gDontYield)
  5141.                             YieldToAnyThread();
  5142.     
  5143.                         // make a new menu or grab the old one
  5144.                         if (mark == 0)
  5145.                             subMenu = NewMenu(mFontSubMenusStart + *pStylesUsed, name);
  5146.                         else
  5147.                             subMenu = GetMenuHandle(mark);
  5148.                         DisposePtr((Ptr) name);
  5149.                         
  5150.                         length = GXFindFontName(fontID, gxStyleFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, nil, nil);
  5151.                         if (length)
  5152.                             {
  5153.                             name = (unsigned char*) NewPtr(length+1);
  5154.                             if (name)
  5155.                                 {
  5156.                                 GXFindFontName(fontID, gxStyleFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, &name[1], nil);
  5157.                                 name[0] = length;
  5158.                                 (void) SortAndAddMenu(subMenu, name);
  5159.                                 DisposePtr((Ptr) name);
  5160.                                 }
  5161.                             }
  5162.                         
  5163.                         // if new menu, add to the master
  5164.                         if (mark == 0)
  5165.                             {
  5166.                             InsertMenu(subMenu, -1);
  5167.                             SetItemCmd(menu, where, hMenuCmd);
  5168.                             SetItemMark(menu, where, mFontSubMenusStart + *pStylesUsed);
  5169.                             (*pStylesUsed)++;
  5170.                             }
  5171.                         }
  5172.                     else
  5173.                         DisposePtr((Ptr) name);
  5174.  
  5175.                     }
  5176.                 }
  5177.                 
  5178.             } // if (*sp == 0)
  5179.             
  5180.         sp += 3;        /* three elements in the FAT */
  5181.         
  5182.         if (!gDontYield)
  5183.             YieldToAnyThread();
  5184.  
  5185.         } // for (# entries)
  5186. } // AddEachEntry
  5187.  
  5188. // --------------------------------------------------------------------------------------------------------------
  5189. // must be in Main because it runs in a thread and we don't want the segment unloaded
  5190. // while some other thread is running
  5191. #pragma segment Main
  5192.  
  5193. static pascal void* FontsThread(void *threadParam)
  5194. {
  5195.     MenuHandle    menu = (MenuHandle) threadParam;
  5196.     long        numberFonts;
  5197.     long        i;
  5198.     short        stylesUsed = 0;
  5199.     gxStyle        theStyle;
  5200.     Boolean        menusAdjusted = false;
  5201.     
  5202.     theStyle = GXNewStyle();
  5203.     
  5204.     numberFonts = CountResources('FOND');
  5205.     for (i = 1; i <= numberFonts; ++i)
  5206.         {
  5207.         Handle     fond = GetIndResource('FOND', i);
  5208.         
  5209.         if (!ResError() && fond && *fond)
  5210.             do
  5211.             {   
  5212.             short* sp = (short*)(*fond + sizeof(FamRec));
  5213.             short entries = *sp++;
  5214.             
  5215.             AddEachEntry(fond, sp, entries, theStyle, menu, &stylesUsed);
  5216.             
  5217.             // now that there are some fonts in the font menu, make sure the menu's enabled
  5218.             if (!menusAdjusted && CountMItems(menu) > 0)
  5219.                 {
  5220.                 AdjustMenus(FrontWindow(), true, false);
  5221.                 menusAdjusted = true;
  5222.                 }
  5223.                 
  5224.             } while ((fond = GetNextFOND(fond)) != 0);
  5225.             
  5226.         } // for (# fonts)
  5227.  
  5228.     GXDisposeStyle(theStyle);
  5229.     
  5230.     gFontThread = kNoThreadID;
  5231.     return 0;
  5232. } // FontsThread
  5233.  
  5234. // --------------------------------------------------------------------------------------------------------------
  5235. #pragma segment Initialize
  5236.  
  5237. static OSErr BuildFontMenu(MenuHandle menu)
  5238. {
  5239.     OSErr    anErr = noErr;
  5240.     
  5241.     if (gMachineInfo.haveGX)
  5242.         {
  5243.         (void) DoStartupGX();
  5244.         
  5245.         // prime the font cache so we don't spend time doing this in the font thread
  5246.         GXGetDefaultFont();
  5247.         
  5248.         anErr = paramErr;
  5249.         if (gMachineInfo.haveThreads)
  5250.             anErr = CreateThread(FontsThread, menu, &gFontThread);
  5251.  
  5252.         if (anErr != noErr)
  5253.             {
  5254.             FontsThread(menu);
  5255.             anErr = noErr;
  5256.             }
  5257.         }
  5258.     else
  5259.         AppendResMenu(menu, 'FONT');
  5260.     
  5261.     return(anErr);
  5262.     
  5263. } // BuildFontMenu
  5264.  
  5265. // --------------------------------------------------------------------------------------------------------------
  5266. #pragma segment Initialize
  5267.  
  5268. static OSErr    DoInitialize(void)
  5269. {
  5270.     short                count;            // loop counter
  5271.     Handle                menuBar;        // for loading our menus in
  5272.     gxGraphicsError        anErr = noErr;    // any errors we get, none so far
  5273.     long                version;        // version for Gestalt calls
  5274.     
  5275.     InitGraf((Ptr) &qd.thePort);
  5276.     InitFonts();
  5277.     InitWindows();
  5278.     InitMenus();
  5279.     TEInit();
  5280.     InitDialogs(nil);
  5281.     InitCursor();
  5282.     
  5283.     gAllDone = false;
  5284.     
  5285.     // check that the system is correct to handle things
  5286.     SysEnvirons(1, &gMachineInfo.theEnvirons);
  5287.     if (gMachineInfo.theEnvirons.systemVersion < 0x0700)
  5288.         {
  5289.         // Wait for app to come to front
  5290.         for (count = 1; count <= 3; ++count)
  5291.             EventAvail(everyEvent, &gEvent);
  5292.             
  5293.         anErr = eMachineToOld;
  5294.         nrequire(anErr, SysEnvirons);
  5295.         }
  5296.  
  5297.     gMachineInfo.lastBalloonIndex = iNoBalloon;
  5298.     gMachineInfo.amInBackground = false;
  5299.     gMachineInfo.documentCount  = 1;
  5300.     gMachineInfo.haveQuickTime     = (Gestalt(gestaltQuickTime, &version) == noErr);
  5301.     gMachineInfo.haveRecording     = (Gestalt(gestaltSoundAttr, &version) == noErr) && ((version & (1<<gestaltHasSoundInputDevice)) != 0);
  5302.     gMachineInfo.haveTTS         = (Gestalt(gestaltSpeechAttr, &version) == noErr) && ((version & (1<<gestaltSpeechMgrPresent)) != 0);
  5303.     gMachineInfo.haveGX            = (Gestalt(gestaltGXVersion, &version) == noErr);
  5304.     gMachineInfo.haveTSM         = (Gestalt(gestaltTSMgrVersion, &version) == noErr) && (version >= 1);
  5305.     gMachineInfo.haveTSMTE         = (Gestalt(gestaltTSMTEAttr, &version) == noErr) && ((version & (1<<gestaltTSMTE)) != 0);
  5306.     gMachineInfo.haveDragMgr    = (Gestalt(gestaltDragMgrAttr, &version) == noErr) && ((version & (1<<gestaltDragMgrPresent)) != 0) &&
  5307.                                     (Gestalt(gestaltTEAttr, &version) == noErr) && ((version & (1<<gestaltTEHasGetHiliteRgn)) != 0);
  5308.     gMachineInfo.haveThreeD        = false;
  5309.     gMachineInfo.haveAppleGuide    = (Gestalt(gestaltHelpMgrAttr, &version) == noErr) && ((version & (1<<gestaltAppleGuidePresent)) != 0);
  5310.     gMachineInfo.haveThreads    = (Gestalt(gestaltThreadMgrAttr, &version) == noErr) && ((version & (1<<gestaltThreadMgrPresent)) != 0);
  5311.     
  5312.     gMachineInfo.haveNavigationServices  = NavServicesAvailable();
  5313.     
  5314.     if ( gMachineInfo.haveNavigationServices ) 
  5315.     {
  5316.         NavLoad();
  5317.     }
  5318.     
  5319.     #if GENERATINGPOWERPC
  5320.         {
  5321.         CFragConnectionID    connID;
  5322.         Ptr                 mainAddr;
  5323.         Str255                errName;
  5324.         
  5325.         if ( (gMachineInfo.haveQuickTime)     && (GetSharedLibrary("\pQuickTimeLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  5326.             gMachineInfo.haveQuickTime = false;
  5327.         if ( (gMachineInfo.haveTTS)         && (GetSharedLibrary("\pSpeechLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  5328.             gMachineInfo.haveTTS = false;
  5329.         if ( (gMachineInfo.haveGX)             && (GetSharedLibrary("\pQuickDrawGXLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  5330.             gMachineInfo.haveGX = false;
  5331.         if ( (gMachineInfo.haveDragMgr)        && (GetSharedLibrary("\pDragLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  5332.             gMachineInfo.haveDragMgr = false;
  5333.         if ( (gMachineInfo.haveThreads)     && (GetSharedLibrary("\pThreadsLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  5334.             gMachineInfo.haveThreads = false;
  5335.         }
  5336.     #endif
  5337.     
  5338.  
  5339.     // initialize text services if they exist
  5340.     if (gMachineInfo.haveTSMTE)
  5341.         {
  5342.         if (InitTSMAwareApplication() != noErr)
  5343.             {
  5344.             gMachineInfo.haveTSM = false;
  5345.             gMachineInfo.haveTSMTE = false;
  5346.             }
  5347.         }
  5348.         
  5349.     // save away info we need from the get-go    
  5350.     gApplicationResFile = CurResFile();
  5351.     gCursorRgn = NewRgn();
  5352.  
  5353.     // load up the menus
  5354.     menuBar = (Handle) GetNewMBar(rMenuBar);            /* read menus into menu bar */
  5355.     anErr = ResError();
  5356.     if ( (anErr == noErr) && (menuBar == nil) )
  5357.         anErr = resNotFound;
  5358.     nrequire(anErr, GetNewMBar);
  5359.     
  5360.     // install menus
  5361.     SetMenuBar(menuBar);    
  5362.     DisposeHandle(menuBar);
  5363.  
  5364.     // build the Apple menu
  5365.     AppendResMenu(GetMenuHandle(mApple), 'DRVR');    /* add DA names to Apple menu */
  5366.     
  5367.     // haven't yet done a startup of QuickDraw GX
  5368.     if (gMachineInfo.haveGX)
  5369.         gMachineInfo.haveStartedGX = false;
  5370.  
  5371.     // Build the font menu
  5372.     anErr = BuildFontMenu(GetMenuHandle(mFont));
  5373.     nrequire(anErr, BuildFontMenu);
  5374.     
  5375.     // insert our heirarchical menus
  5376.     {
  5377.     MenuHandle     menu = GetMenu( mVoices );
  5378.     short        menuID, itemID;
  5379.     
  5380.     InsertMenu( menu, hierMenu );
  5381.     
  5382.     CommandToIDs(cSelectVoice, &menuID, &itemID);
  5383.     menu = GetMenuHandle(menuID);
  5384.  
  5385.     SetItemCmd( menu, itemID, hMenuCmd );
  5386.     SetItemMark( menu, itemID, mVoices );
  5387.     }
  5388.  
  5389.     AdjustMenus(nil, true, false);
  5390.     DrawMenuBar();
  5391.     
  5392.     // start up QuickTime, but problems result in us pretending not to have it
  5393.     if (gMachineInfo.haveQuickTime)
  5394.         if (EnterMovies() != noErr)
  5395.             gMachineInfo.haveQuickTime = false;
  5396.         
  5397.     // Install AppleEvent handlers for the base classes
  5398.  
  5399.     #define INSTALL(event, handler) \
  5400.             AEInstallEventHandler(kCoreEventClass, event, handler, 0, false)
  5401.  
  5402.     INSTALL (kAEOpenApplication, gDoOpenApp);
  5403.     INSTALL (kAEQuitApplication, gDoQuitApp);
  5404.     INSTALL (kAEOpenDocuments,   gDoOpenDocument);
  5405.     INSTALL (kAEPrintDocuments,  gDoPrintDocument);
  5406.  
  5407.     #undef INSTALL
  5408.  
  5409.     // Install our global dragging procs, but only if we have Drag and Drop. An error results
  5410.     // in us pretending that we don't have drag support. Notice that in the test above, we also
  5411.     // require TextEdit to have TEGetHiliteRgn avalilable, which is always the case with the
  5412.     // present Drag Manager.
  5413.  
  5414.     if (gMachineInfo.haveDragMgr)
  5415.         {
  5416.         gGlobalTrackingHandler = NewDragTrackingHandlerProc(GlobalTrackingHandler);
  5417.         gGlobalReceiveHandler = NewDragReceiveHandlerProc(GlobalReceiveHandler);
  5418.         
  5419.         anErr = InstallTrackingHandler(gGlobalTrackingHandler, nil, nil);
  5420.  
  5421.         if (anErr == noErr)
  5422.             {
  5423.             anErr = InstallReceiveHandler(gGlobalReceiveHandler, nil, nil);
  5424.  
  5425.             if (anErr != noErr)
  5426.                 {
  5427.                 RemoveTrackingHandler(gGlobalTrackingHandler, nil);
  5428.                 gMachineInfo.haveDragMgr = false;
  5429.                 }
  5430.             }
  5431.         else
  5432.             gMachineInfo.haveDragMgr = false;
  5433.         }
  5434.  
  5435.  
  5436.     // verify that the AppleGuide database is available, and if it isn't, try to find
  5437.     // it from other places -- but don't bother on 2.1, because they made changes
  5438.     // to break our location finding (sigh!)
  5439.     if  (
  5440.         (gMachineInfo.haveAppleGuide) && 
  5441.         ( (Gestalt('ag_v', &version) != noErr) || (version < 0x00000210) )
  5442.         )
  5443.         FindAndInstallSimpleTextGuide();
  5444.         
  5445.  
  5446.     return noErr;
  5447.     
  5448.     
  5449. // EXCEPTION HANDLING
  5450. BuildFontMenu:
  5451. GetNewMBar:
  5452. SysEnvirons:
  5453.     ConductErrorDialog(anErr, cNull, cancel);
  5454.     
  5455.     return anErr;
  5456.  
  5457. } // DoInitialize
  5458.  
  5459. // --------------------------------------------------------------------------------------------------------------
  5460. #pragma segment Terminate
  5461.  
  5462. static OSErr    DoTerminate(void)
  5463. {
  5464.     OSErr    anErr = noErr;
  5465.     
  5466.     if (gFontThread != kNoThreadID)
  5467.         DisposeThread(gFontThread, &gThreadResults, false);
  5468.     if (gAGThread != kNoThreadID)
  5469.         DisposeThread(gAGThread, &gThreadResults, false);
  5470.     if (gStarterThread != kNoThreadID)
  5471.         DisposeThread(gStarterThread, &gThreadResults, false);
  5472.  
  5473.     if (gMachineInfo.haveQuickTime)
  5474.         ExitMovies();
  5475.  
  5476.     if ( (gMachineInfo.haveGX) && (GXGetGraphicsClient() != nil) )
  5477.         {
  5478.         GXExitPrinting();
  5479.         GXExitGraphics();
  5480.         }
  5481.         
  5482.     if (gMachineInfo.haveTSMTE)
  5483.         CloseTSMAwareApplication();
  5484.  
  5485.     if (gMachineInfo.haveDragMgr)
  5486.         {
  5487.         RemoveReceiveHandler(gGlobalReceiveHandler, nil);
  5488.         RemoveTrackingHandler(gGlobalTrackingHandler, nil);
  5489.         }
  5490.         
  5491.     if (gMachineInfo.haveAppleGuide)
  5492.         {
  5493.         if ((gAGRefNum != -1) && AGIsDatabaseOpen(gAGRefNum))
  5494.             {
  5495.             AGClose(&gAGRefNum);
  5496.             gAGRefNum = -1;
  5497.             }
  5498.         if (gAGCoachRefNum != -1)
  5499.             AGRemoveCoachHandler(&gAGCoachRefNum);
  5500.         }
  5501.  
  5502.     if ( gMachineInfo.haveNavigationServices ) 
  5503.     {
  5504.         NavUnload();
  5505.     }
  5506.  
  5507.     return anErr;
  5508.     
  5509. } // DoTerminate
  5510.  
  5511. // --------------------------------------------------------------------------------------------------------------
  5512. #pragma segment Main
  5513.  
  5514. main(void)
  5515. {
  5516.     OSErr    anErr;
  5517.     
  5518. #ifndef __MWERKS__
  5519.     UnloadSeg((Ptr) _DataInit);                        /* note that _DataInit must not be in Main! */
  5520. #endif
  5521.     MaxApplZone();                                    /* expand the heap so code segments load at the top */
  5522.     MoreMasters(); MoreMasters(); MoreMasters();     /* we love handles */
  5523.     anErr = DoInitialize();
  5524.     UnloadSeg((Ptr) DoInitialize);                    
  5525.     if (anErr == noErr)
  5526.         {
  5527.         DoEventLoop();
  5528.  
  5529. // REVIEW: don't want to unload the segment we're in!!
  5530. //        UnloadSeg((Ptr) DoEventLoop);
  5531.         DoTerminate();                    
  5532.         }
  5533.     return 0;
  5534. } // main
  5535.